Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S936495AbdIYWSd (ORCPT ); Mon, 25 Sep 2017 18:18:33 -0400 Received: from mail-ua0-f196.google.com ([209.85.217.196]:37017 "EHLO mail-ua0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S935300AbdIYWSb (ORCPT ); Mon, 25 Sep 2017 18:18:31 -0400 X-Google-Smtp-Source: AOwi7QCx/8V9dZPyo9i1mJdPYY6z/LtIj7cCw3TxCpxAtkVVb3sNc0O0KFqHsuvdb0Uy3733bADi0fu5tqIkT0zNsNs= MIME-Version: 1.0 From: Kyle Huey Date: Mon, 25 Sep 2017 15:18:29 -0700 Message-ID: Subject: Regression related to ipc shmctl compat To: open list , Al Viro Cc: "Robert O'Callahan" Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 5161 Lines: 187 Beginning with 553f770ef71b, the following program fails when compiled for 32 bit and executed on a 64 bit kernel and succeeds when compiled for and executed on a 64 bit. It continues to fail even after 58aff0af7573. When compiled as 32 bit, an shmctl call fails with EBADR (see the XXX comment). The test program is adapted from rr's shm test[0]. #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include static uint64_t GUARD_VALUE = 0xdeadbeeff00dbaad; inline static size_t ceil_page_size(size_t size) { size_t page_size = sysconf(_SC_PAGESIZE); return (size + page_size - 1) & ~(page_size - 1); } /** * Allocate 'size' bytes, fill with 'value', place canary value before * the allocated block, and put guard pages before and after. Ensure * there's a guard page immediately after `size`. * This lets us catch cases where too much data is being recorded --- which can * cause errors if the recorder tries to read invalid memory. */ inline static void* allocate_guard(size_t size, char value) { size_t page_size = sysconf(_SC_PAGESIZE); size_t map_size = ceil_page_size(size + sizeof(GUARD_VALUE)) + 2 * page_size; char* cp = (char*)mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); assert(cp != MAP_FAILED); /* create guard pages */ assert(munmap(cp, page_size) == 0); assert(munmap(cp + map_size - page_size, page_size) == 0); cp = cp + map_size - page_size - size; memcpy(cp - sizeof(GUARD_VALUE), &GUARD_VALUE, sizeof(GUARD_VALUE)); memset(cp, value, size); return cp; } /** * Verify that canary value before the block allocated at 'p' * (of size 'size') is still valid. */ inline static void verify_guard(__attribute__((unused)) size_t size, void* p) { char* cp = (char*)p; assert(memcmp(cp - sizeof(GUARD_VALUE), &GUARD_VALUE, sizeof(GUARD_VALUE)) == 0); } /** * Verify that canary value before the block allocated at 'p' * (of size 'size') is still valid, and free the block. */ inline static void free_guard(size_t size, void* p) { verify_guard(size, p); size_t page_size = sysconf(_SC_PAGESIZE); size_t map_size = ceil_page_size(size + sizeof(GUARD_VALUE)) + 2 * page_size; char* cp = (char*)p + size + page_size - map_size; assert(0 == munmap(cp, map_size - 2 * page_size)); } #define ALLOCATE_GUARD(p, v) p = allocate_guard(sizeof(*p), v) #define VERIFY_GUARD(p) verify_guard(sizeof(*p), p) #define FREE_GUARD(p) free_guard(sizeof(*p), p) /* Make SIZE not a multiple of the page size, to ensure we handle that case. But make sure it's even, since we divide it by two. */ #define SIZE ((int)(16 * page_size) - 10) static int shmid; static void before_writing(void) {} static void after_writing(void) {} static int run_child(void) { int i; char* p; char* p2; pid_t child2; int status; struct shmid_ds* ds; struct shminfo* info; struct shm_info* info2; size_t page_size = sysconf(_SC_PAGESIZE); ALLOCATE_GUARD(ds, 'd'); assert(0 == shmctl(shmid, IPC_STAT, ds)); VERIFY_GUARD(ds); assert((int)ds->shm_segsz == SIZE); assert(ds->shm_cpid == getppid()); assert(ds->shm_nattch == 0); ds->shm_perm.mode = 0660; assert(0 == shmctl(shmid, IPC_SET, ds)); ALLOCATE_GUARD(info, 'i'); assert(0 <= shmctl(shmid, IPC_INFO, (struct shmid_ds*)info)); VERIFY_GUARD(info); assert(info->shmmin == 1); ALLOCATE_GUARD(info2, 'j'); // XXX: This shmctl call fails with EBADR when compiled with -m32 assert(0 <= shmctl(shmid, SHM_INFO, (struct shmid_ds*)info2)); VERIFY_GUARD(info2); assert(info2->used_ids > 0); assert(info2->used_ids < 1000000); p = shmat(shmid, NULL, 0); assert(p != (char*)-1); before_writing(); for (i = 0; i < SIZE; ++i) { assert(p[i] == 0); } memset(p, 'r', SIZE / 2); after_writing(); p2 = shmat(shmid, NULL, 0); assert(p2 != (char*)-1); memset(p + SIZE / 2, 'r', SIZE / 2); assert(0 == shmdt(p)); assert(0 == shmdt(p2)); assert(p == mmap(p, SIZE, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); assert(p[0] == 0); p = shmat(shmid, p, SHM_REMAP); assert(p != (char*)-1); for (i = 0; i < SIZE; ++i) { assert(p[i] == 'r'); } if ((child2 = fork()) == 0) { memset(p, 's', SIZE); return 0; } assert(child2 == waitpid(child2, &status, __WALL)); assert(0 == status); for (i = 0; i < SIZE; ++i) { assert(p[i] == 's'); } return 0; } int main(void) { pid_t child; int status; size_t page_size = sysconf(_SC_PAGESIZE); shmid = shmget(IPC_PRIVATE, SIZE, 0666); assert(shmid >= 0); if ((child = fork()) == 0) { return run_child(); } printf("child %d\n", child); assert(child == waitpid(child, &status, __WALL)); /* delete the shm before testing status, because we want to ensure the segment is deleted even if the test failed. */ assert(0 == shmctl(shmid, IPC_RMID, NULL)); assert(status == 0); printf("EXIT-SUCCESS\n"); return 0; } - Kyle [0] https://github.com/mozilla/rr/blob/master/src/test/shm.c