mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-09-04 18:49:41 +00:00
selftests/mm: confirm VA exhaustion without reliance on correctness of mmap()
Currently, VA exhaustion is being checked by passing a hint to mmap() and expecting it to fail. While populating the lower VA space, mmap() fails because we have exhausted the space. Then, in validate_lower_address_hint(), because mmap() fails, we confirm that we have indeed exhausted the space. There is a circular logic involved here. Assume that there is a bug in mmap(), also assume that it exists independent of whether you pass a hint address or not; that for some reason it is not able to find a 1GB chunk. My idea is to assert the exhaustion against some other method. This patch makes a stricter test by successful write() calls from /proc/self/maps to a dump file, confirming that a free chunk is indeed not available. [dev.jain@arm.com: replace SZ_1GB with MAP_CHUNK_SIZE, tidy-up] Link: https://lkml.kernel.org/r/20240325042653.867055-1-dev.jain@arm.com Link: https://lkml.kernel.org/r/20240321103522.516097-1-dev.jain@arm.com Signed-off-by: Dev Jain <dev.jain@arm.com> Cc: Anshuman Khandual <anshuman.khandual@arm.com> Cc: Shuah Khan <shuah@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
parent
42a346b41c
commit
0104096498
@ -12,6 +12,8 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include "../kselftest.h"
|
#include "../kselftest.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -93,6 +95,66 @@ static int validate_lower_address_hint(void)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int validate_complete_va_space(void)
|
||||||
|
{
|
||||||
|
unsigned long start_addr, end_addr, prev_end_addr;
|
||||||
|
char line[400];
|
||||||
|
char prot[6];
|
||||||
|
FILE *file;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = open("va_dump", O_CREAT | O_WRONLY, 0600);
|
||||||
|
unlink("va_dump");
|
||||||
|
if (fd < 0) {
|
||||||
|
ksft_test_result_skip("cannot create or open dump file\n");
|
||||||
|
ksft_finished();
|
||||||
|
}
|
||||||
|
|
||||||
|
file = fopen("/proc/self/maps", "r");
|
||||||
|
if (file == NULL)
|
||||||
|
ksft_exit_fail_msg("cannot open /proc/self/maps\n");
|
||||||
|
|
||||||
|
prev_end_addr = 0;
|
||||||
|
while (fgets(line, sizeof(line), file)) {
|
||||||
|
unsigned long hop;
|
||||||
|
|
||||||
|
if (sscanf(line, "%lx-%lx %s[rwxp-]",
|
||||||
|
&start_addr, &end_addr, prot) != 3)
|
||||||
|
ksft_exit_fail_msg("cannot parse /proc/self/maps\n");
|
||||||
|
|
||||||
|
/* end of userspace mappings; ignore vsyscall mapping */
|
||||||
|
if (start_addr & (1UL << 63))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* /proc/self/maps must have gaps less than MAP_CHUNK_SIZE */
|
||||||
|
if (start_addr - prev_end_addr >= MAP_CHUNK_SIZE)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
prev_end_addr = end_addr;
|
||||||
|
|
||||||
|
if (prot[0] != 'r')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Confirm whether MAP_CHUNK_SIZE chunk can be found or not.
|
||||||
|
* If write succeeds, no need to check MAP_CHUNK_SIZE - 1
|
||||||
|
* addresses after that. If the address was not held by this
|
||||||
|
* process, write would fail with errno set to EFAULT.
|
||||||
|
* Anyways, if write returns anything apart from 1, exit the
|
||||||
|
* program since that would mean a bug in /proc/self/maps.
|
||||||
|
*/
|
||||||
|
hop = 0;
|
||||||
|
while (start_addr + hop < end_addr) {
|
||||||
|
if (write(fd, (void *)(start_addr + hop), 1) != 1)
|
||||||
|
return 1;
|
||||||
|
lseek(fd, 0, SEEK_SET);
|
||||||
|
|
||||||
|
hop += MAP_CHUNK_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
char *ptr[NR_CHUNKS_LOW];
|
char *ptr[NR_CHUNKS_LOW];
|
||||||
@ -133,6 +195,10 @@ int main(int argc, char *argv[])
|
|||||||
validate_addr(hptr[i], 1);
|
validate_addr(hptr[i], 1);
|
||||||
}
|
}
|
||||||
hchunks = i;
|
hchunks = i;
|
||||||
|
if (validate_complete_va_space()) {
|
||||||
|
ksft_test_result_fail("BUG in mmap() or /proc/self/maps\n");
|
||||||
|
ksft_finished();
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < lchunks; i++)
|
for (i = 0; i < lchunks; i++)
|
||||||
munmap(ptr[i], MAP_CHUNK_SIZE);
|
munmap(ptr[i], MAP_CHUNK_SIZE);
|
||||||
|
Loading…
Reference in New Issue
Block a user