From patchwork Fri Sep 11 03:55:33 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Li Wang X-Patchwork-Id: 1362220 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.linux.it (client-ip=2001:1418:10:5::2; helo=picard.linux.it; envelope-from=ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=UbFrex4D; dkim-atps=neutral Received: from picard.linux.it (picard.linux.it [IPv6:2001:1418:10:5::2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4BnhkR36s0z9sTg for ; Fri, 11 Sep 2020 13:55:57 +1000 (AEST) Received: from picard.linux.it (localhost [IPv6:::1]) by picard.linux.it (Postfix) with ESMTP id 06F7C3C527D for ; Fri, 11 Sep 2020 05:55:48 +0200 (CEST) X-Original-To: ltp@lists.linux.it Delivered-To: ltp@picard.linux.it Received: from in-3.smtp.seeweb.it (in-3.smtp.seeweb.it [IPv6:2001:4b78:1:20::3]) by picard.linux.it (Postfix) with ESMTP id 1F2423C224B for ; Fri, 11 Sep 2020 05:55:45 +0200 (CEST) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [63.128.21.124]) by in-3.smtp.seeweb.it (Postfix) with ESMTP id 5E77E1A00141 for ; Fri, 11 Sep 2020 05:55:43 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1599796542; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=eors4/7M8b2V5Rxa7+W7i8lli4N2GvZy/5dXinaUo8g=; b=UbFrex4DSdKb2brMfnutyGssCxYShgr4TMYcue4qD9pSdR2eJazjCYys9GYXqInOfHGgGq Qy0irTDS6Vkudenp9vyAmusQ8cDmGnuL8oA7JdWWlcy6BhivJ86KVCmM/M2C4N2Pdw138r TJkD3qSgWOkW5XBL4v9TTIzbJJYKEjA= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-596-sPxIGCc-OdCu7X3fmQOr6w-1; Thu, 10 Sep 2020 23:55:38 -0400 X-MC-Unique: sPxIGCc-OdCu7X3fmQOr6w-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id B5E1E1007B16; Fri, 11 Sep 2020 03:55:36 +0000 (UTC) Received: from liwang-workstation.nay.redhat.com (unknown [10.66.81.246]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2416D19C4F; Fri, 11 Sep 2020 03:55:34 +0000 (UTC) From: Li Wang To: ltp@lists.linux.it Date: Fri, 11 Sep 2020 03:55:33 +0000 Message-Id: <20200911035533.30538-1-liwang@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=liwang@redhat.com X-Mimecast-Spam-Score: 0.002 X-Mimecast-Originator: redhat.com X-Virus-Scanned: clamav-milter 0.102.4 at in-3.smtp.seeweb.it X-Virus-Status: Clean X-Spam-Status: No, score=0.1 required=7.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,DKIM_VALID_EF,SPF_HELO_NONE,SPF_PASS autolearn=disabled version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on in-3.smtp.seeweb.it Subject: [LTP] [PATCH v5] Add a test case for mmap MAP_GROWSDOWN flag X-BeenThere: ltp@lists.linux.it X-Mailman-Version: 2.1.29 Precedence: list List-Id: Linux Test Project List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pravin , "Vijay Kumar B ." Errors-To: ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it Sender: "ltp" From: pravin We assign the memory region allocated using MAP_GROWSDOWN to a thread, as a stack, to test the effect of MAP_GROWSDOWN. This is because the kernel only grows the memory region when the stack pointer, is within guard page, when the guard page is touched. 1. Map an anyonymous memory region of size X, and unmap it. 2. Split the unmapped memory region into two. 3. The lower memory region is left unmapped. 4. The higher memory region is mapped for use as stack, using MAP_FIXED | MAP_GROWSDOWN. 5. The higher memory region is provided as stack to a thread, where a recursive function is invoked. 6. The stack grows beyond the allocated region, into the lower memory area. 7. If this results in the memory region being extended, into the unmapped region, the test is considered to have passed. Also, to verify that(Test2) the stack grows to within a page of the high end of the next lower map‐ping will result in a SIGSEGV signal. Resolves #300 Signed-off-by: Pravin Raghul S. Reviewed-by: Vijay Kumar B. Signed-off-by: Li Wang Cc: Cyril Hrubis Reviewed-by: Petr Vorel --- runtest/syscalls | 1 + testcases/kernel/syscalls/mmap/.gitignore | 1 + testcases/kernel/syscalls/mmap/mmap18.c | 177 ++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 testcases/kernel/syscalls/mmap/mmap18.c diff --git a/runtest/syscalls b/runtest/syscalls index dc0ca5626..ed86bb593 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -747,6 +747,7 @@ mmap14 mmap14 mmap15 mmap15 mmap16 mmap16 mmap17 mmap17 +mmap18 mmap18 modify_ldt01 modify_ldt01 modify_ldt02 modify_ldt02 diff --git a/testcases/kernel/syscalls/mmap/.gitignore b/testcases/kernel/syscalls/mmap/.gitignore index c5c083d4b..4fd90ab5f 100644 --- a/testcases/kernel/syscalls/mmap/.gitignore +++ b/testcases/kernel/syscalls/mmap/.gitignore @@ -16,3 +16,4 @@ /mmap15 /mmap16 /mmap17 +/mmap18 diff --git a/testcases/kernel/syscalls/mmap/mmap18.c b/testcases/kernel/syscalls/mmap/mmap18.c new file mode 100644 index 000000000..b5008497d --- /dev/null +++ b/testcases/kernel/syscalls/mmap/mmap18.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) Zilogic Systems Pvt. Ltd., 2020 + * Email: code@zilogic.com + */ + +/* + * Test mmap() MAP_GROWSDOWN flag + * + * # Test1: + * We assign the memory region allocated using MAP_GROWSDOWN to a thread, + * as a stack, to test the effect of MAP_GROWSDOWN. This is because the + * kernel only grows the memory region when the stack pointer, is within + * guard page, when the guard page is touched. + * + * 1. Map an anyonymous memory region of size X, and unmap it. + * 2. Split the unmapped memory region into two. + * 3. The lower memory region is left unmapped. + * 4. The higher memory region is mapped for use as stack, using + * MAP_FIXED | MAP_GROWSDOWN. + * 5. The higher memory region is provided as stack to a thread, where + * a recursive function is invoked. + * 6. The stack grows beyond the allocated region, into the lower memory area. + * 7. If this results in the memory region being extended, into the + * unmapped region, the test is considered to have passed. + * + * # Test2: + * Steps mostly like Test1, but mmaping a page in the space the stack is + * supposed to grow into. To verify that the stack grows to within a page + * of the high end of the next lower map‐ping, at which point touching + * the "guard" page will result in a SIGSEGV signal. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "tst_test.h" +#include "tst_safe_pthread.h" + +#define UNITS(x) ((x) * PTHREAD_STACK_MIN) + +static void *stack; +static long stack_size = UNITS(8); + +static bool __attribute__((noinline)) check_stackgrow_up(void) +{ + char local_var; + static char *addr; + + if (!addr) { + addr = &local_var; + return check_stackgrow_up(); + } + + return (addr < &local_var); +} + +static void setup(void) +{ + if (check_stackgrow_up()) + tst_brk(TCONF, "Test can't be performed with stack grows up architecture"); +} + +static void allocate_stack(size_t size) +{ + void *start; + + /* + * Since the newer kernel does not allow a MAP_GROWSDOWN mapping to grow + * closer than 'stack_guard_gap' pages away from a preceding mapping. + * The guard then ensures that the next-highest mapped page remains more + * than 'stack_guard_gap' below the lowest stack address, and if not then + * it will trigger a segfault. So, here adding 256 pages memory spacing + * for stack growing safely. + * + * Btw, kernel default 'stack_guard_gap' size is '256 * getpagesize()'. + */ + long total_size = 256 * getpagesize() + size * 2; + + start = SAFE_MMAP(NULL, total_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + SAFE_MUNMAP(start, total_size); + + /* start stack + * +-----------+---------------------+----------------------+ + * | 256 pages | unmapped (size) | mapped (size) | + * +-----------+---------------------+----------------------+ + * + */ + stack = SAFE_MMAP((start + total_size - size), size, PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN, + -1, 0); + + tst_res(TINFO, "start = %p, stack = %p", start, stack); +} + +static __attribute__((noinline)) void *check_depth_recursive(void *limit) +{ + if ((off_t) &limit < (off_t) limit) { + tst_res(TINFO, "&limit = %p, limit = %p", &limit, limit); + return NULL; + } + + return check_depth_recursive(limit); +} + +static void grow_stack(void *stack, size_t size, void *limit) +{ + pthread_t test_thread; + pthread_attr_t attr; + int ret; + + ret = pthread_attr_init(&attr); + if (ret) + tst_brk(TBROK, "pthread_attr_init failed during setup"); + + ret = pthread_attr_setstack(&attr, stack, size); + if (ret) + tst_brk(TBROK, "pthread_attr_setstack failed during setup"); + + SAFE_PTHREAD_CREATE(&test_thread, &attr, check_depth_recursive, limit); + SAFE_PTHREAD_JOIN(test_thread, NULL); + + if (stack) + SAFE_MUNMAP(stack, stack_size); + + exit(0); +} + +static void run_test(void) +{ + pid_t child_pid; + int wstatus; + + /* Test 1 */ + child_pid = SAFE_FORK(); + if (!child_pid) { + allocate_stack(stack_size); + grow_stack(stack, stack_size, stack - stack_size + UNITS(1)); + } + + SAFE_WAIT(&wstatus); + if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) + tst_res(TPASS, "Stack grows in unmapped region"); + else + tst_res(TFAIL, "Child: %s", tst_strstatus(wstatus)); + + /* Test 2 */ + child_pid = SAFE_FORK(); + if (!child_pid) { + tst_no_corefile(0); + allocate_stack(stack_size); + + SAFE_MMAP(stack - stack_size, UNITS(1), PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + /* This will cause to segment fault (SIGSEGV) */ + grow_stack(stack, stack_size, stack - stack_size + UNITS(1)); + } + + SAFE_WAIT(&wstatus); + if (WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGSEGV) + tst_res(TPASS, "Child ended by %s as expected", tst_strsig(SIGSEGV)); + else + tst_res(TFAIL, "Child: %s", tst_strstatus(wstatus)); +} + +static struct tst_test test = { + .setup = setup, + .test_all = run_test, + .forks_child = 1, +};