Commit 573a2593 authored by Axel Rasmussen's avatar Axel Rasmussen Committed by Linus Torvalds
Browse files

userfaultfd: selftests: fix SIGSEGV if huge mmap fails



The error handling in hugetlb_allocate_area() was incorrect for the
hugetlb_shared test case.

Previously the behavior was:

- mmap a hugetlb area
  - If this fails, set the pointer to NULL, and carry on
- mmap an alias of the same hugetlb fd
  - If this fails, munmap the original area

If the original mmap failed, it's likely the second one did too.  If
both failed, we'd blindly try to munmap a NULL pointer, causing a
SIGSEGV.  Instead, "goto fail" so we return before trying to mmap the
alias.

This issue can be hit "in real life" by forgetting to set
/proc/sys/vm/nr_hugepages (leaving it at 0), and then trying to run the
hugetlb_shared test.

Another small improvement is, when the original mmap fails, don't just
print "it failed": perror(), so we can see *why*.  :)

Signed-off-by: default avatarAxel Rasmussen <axelrasmussen@google.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Peter Xu <peterx@redhat.com>
Cc: Joe Perches <joe@perches.com>
Cc: Mike Rapoport <rppt@linux.vnet.ibm.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: David Alan Gilbert <dgilbert@redhat.com>
Link: https://lkml.kernel.org/r/20201204203443.2714693-1-axelrasmussen@google.com


Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent d8cbe8bf
Loading
Loading
Loading
Loading
+16 −9
Original line number Diff line number Diff line
@@ -206,19 +206,19 @@ static int hugetlb_release_pages(char *rel_area)
	return ret;
}


static void hugetlb_allocate_area(void **alloc_area)
{
	void *area_alias = NULL;
	char **alloc_area_alias;

	*alloc_area = mmap(NULL, nr_pages * page_size, PROT_READ | PROT_WRITE,
			   (map_shared ? MAP_SHARED : MAP_PRIVATE) |
			   MAP_HUGETLB,
			   huge_fd, *alloc_area == area_src ? 0 :
			   nr_pages * page_size);
	if (*alloc_area == MAP_FAILED) {
		fprintf(stderr, "mmap of hugetlbfs file failed\n");
		*alloc_area = NULL;
		perror("mmap of hugetlbfs file failed");
		goto fail;
	}

	if (map_shared) {
@@ -227,14 +227,11 @@ static void hugetlb_allocate_area(void **alloc_area)
				  huge_fd, *alloc_area == area_src ? 0 :
				  nr_pages * page_size);
		if (area_alias == MAP_FAILED) {
			if (munmap(*alloc_area, nr_pages * page_size) < 0) {
				perror("hugetlb munmap");
				exit(1);
			}
			*alloc_area = NULL;
			return;
			perror("mmap of hugetlb file alias failed");
			goto fail_munmap;
		}
	}

	if (*alloc_area == area_src) {
		huge_fd_off0 = *alloc_area;
		alloc_area_alias = &area_src_alias;
@@ -243,6 +240,16 @@ static void hugetlb_allocate_area(void **alloc_area)
	}
	if (area_alias)
		*alloc_area_alias = area_alias;

	return;

fail_munmap:
	if (munmap(*alloc_area, nr_pages * page_size) < 0) {
		perror("hugetlb munmap");
		exit(1);
	}
fail:
	*alloc_area = NULL;
}

static void hugetlb_alias_mapping(__u64 *start, size_t len, unsigned long offset)