Merge branch 'akpm' (patches from Andrew)
Merge more updates from Andrew Morton: "VM: - z3fold fixes and enhancements by Henry Burns and Vitaly Wool - more accurate reclaimed slab caches calculations by Yafang Shao - fix MAP_UNINITIALIZED UAPI symbol to not depend on config, by Christoph Hellwig - !CONFIG_MMU fixes by Christoph Hellwig - new novmcoredd parameter to omit device dumps from vmcore, by Kairui Song - new test_meminit module for testing heap and pagealloc initialization, by Alexander Potapenko - ioremap improvements for huge mappings, by Anshuman Khandual - generalize kprobe page fault handling, by Anshuman Khandual - device-dax hotplug fixes and improvements, by Pavel Tatashin - enable synchronous DAX fault on powerpc, by Aneesh Kumar K.V - add pte_devmap() support for arm64, by Robin Murphy - unify locked_vm accounting with a helper, by Daniel Jordan - several misc fixes core/lib: - new typeof_member() macro including some users, by Alexey Dobriyan - make BIT() and GENMASK() available in asm, by Masahiro Yamada - changed LIST_POISON2 on x86_64 to 0xdead000000000122 for better code generation, by Alexey Dobriyan - rbtree code size optimizations, by Michel Lespinasse - convert struct pid count to refcount_t, by Joel Fernandes get_maintainer.pl: - add --no-moderated switch to skip moderated ML's, by Joe Perches misc: - ptrace PTRACE_GET_SYSCALL_INFO interface - coda updates - gdb scripts, various" [ Using merge message suggestion from Vlastimil Babka, with some editing - Linus ] * emailed patches from Andrew Morton <akpm@linux-foundation.org>: (100 commits) fs/select.c: use struct_size() in kmalloc() mm: add account_locked_vm utility function arm64: mm: implement pte_devmap support mm: introduce ARCH_HAS_PTE_DEVMAP mm: clean up is_device_*_page() definitions mm/mmap: move common defines to mman-common.h mm: move MAP_SYNC to asm-generic/mman-common.h device-dax: "Hotremove" persistent memory that is used like normal RAM mm/hotplug: make remove_memory() interface usable device-dax: fix memory and resource leak if hotplug fails include/linux/lz4.h: fix spelling and copy-paste errors in documentation ipc/mqueue.c: only perform resource calculation if user valid include/asm-generic/bug.h: fix "cut here" for WARN_ON for __WARN_TAINT architectures scripts/gdb: add helpers to find and list devices scripts/gdb: add lx-genpd-summary command drivers/pps/pps.c: clear offset flags in PPS_SETPARAMS ioctl kernel/pid.c: convert struct pid count to refcount_t drivers/rapidio/devices/rio_mport_cdev.c: NUL terminate some strings select: shift restore_saved_sigmask_unless() into poll_select_copy_remaining() select: change do_poll() to return -ERESTARTNOHAND rather than -EINTR ...
This commit is contained in:
1
tools/testing/selftests/proc/.gitignore
vendored
1
tools/testing/selftests/proc/.gitignore
vendored
@@ -12,4 +12,5 @@
|
||||
/read
|
||||
/self
|
||||
/setns-dcache
|
||||
/setns-sysvipc
|
||||
/thread-self
|
||||
|
@@ -17,6 +17,7 @@ TEST_GEN_PROGS += proc-uptime-002
|
||||
TEST_GEN_PROGS += read
|
||||
TEST_GEN_PROGS += self
|
||||
TEST_GEN_PROGS += setns-dcache
|
||||
TEST_GEN_PROGS += setns-sysvipc
|
||||
TEST_GEN_PROGS += thread-self
|
||||
|
||||
include ../lib.mk
|
||||
|
@@ -215,6 +215,11 @@ static const char str_vsyscall[] =
|
||||
"ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n";
|
||||
|
||||
#ifdef __x86_64__
|
||||
static void sigaction_SIGSEGV(int _, siginfo_t *__, void *___)
|
||||
{
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* vsyscall page can't be unmapped, probe it with memory load.
|
||||
*/
|
||||
@@ -231,11 +236,19 @@ static void vsyscall(void)
|
||||
if (pid == 0) {
|
||||
struct rlimit rlim = {0, 0};
|
||||
(void)setrlimit(RLIMIT_CORE, &rlim);
|
||||
|
||||
/* Hide "segfault at ffffffffff600000" messages. */
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(struct sigaction));
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
act.sa_sigaction = sigaction_SIGSEGV;
|
||||
(void)sigaction(SIGSEGV, &act, NULL);
|
||||
|
||||
*(volatile int *)0xffffffffff600000UL;
|
||||
exit(0);
|
||||
}
|
||||
wait(&wstatus);
|
||||
if (WIFEXITED(wstatus)) {
|
||||
waitpid(pid, &wstatus, 0);
|
||||
if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) {
|
||||
g_vsyscall = true;
|
||||
}
|
||||
}
|
||||
|
133
tools/testing/selftests/proc/setns-sysvipc.c
Normal file
133
tools/testing/selftests/proc/setns-sysvipc.c
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright © 2019 Alexey Dobriyan <adobriyan@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
/*
|
||||
* Test that setns(CLONE_NEWIPC) points to new /proc/sysvipc content even
|
||||
* if old one is in dcache.
|
||||
*/
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
|
||||
static pid_t pid = -1;
|
||||
|
||||
static void f(void)
|
||||
{
|
||||
if (pid > 0) {
|
||||
kill(pid, SIGTERM);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fd[2];
|
||||
char _ = 0;
|
||||
int nsfd;
|
||||
|
||||
atexit(f);
|
||||
|
||||
/* Check for priviledges and syscall availability straight away. */
|
||||
if (unshare(CLONE_NEWIPC) == -1) {
|
||||
if (errno == ENOSYS || errno == EPERM) {
|
||||
return 4;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/* Distinguisher between two otherwise empty IPC namespaces. */
|
||||
if (shmget(IPC_PRIVATE, 1, IPC_CREAT) == -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pipe(fd) == -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
if (unshare(CLONE_NEWIPC) == -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (write(fd[1], &_, 1) != 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
pause();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (read(fd[0], &_, 1) != 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
{
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "/proc/%u/ns/ipc", pid);
|
||||
nsfd = open(buf, O_RDONLY);
|
||||
if (nsfd == -1) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reliably pin dentry into dcache. */
|
||||
(void)open("/proc/sysvipc/shm", O_RDONLY);
|
||||
|
||||
if (setns(nsfd, CLONE_NEWIPC) == -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
kill(pid, SIGTERM);
|
||||
pid = 0;
|
||||
|
||||
{
|
||||
char buf[4096];
|
||||
ssize_t rv;
|
||||
int fd;
|
||||
|
||||
fd = open("/proc/sysvipc/shm", O_RDONLY);
|
||||
if (fd == -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define S32 " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n"
|
||||
#define S64 " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n"
|
||||
rv = read(fd, buf, sizeof(buf));
|
||||
if (rv == strlen(S32)) {
|
||||
assert(memcmp(buf, S32, strlen(S32)) == 0);
|
||||
} else if (rv == strlen(S64)) {
|
||||
assert(memcmp(buf, S64, strlen(S64)) == 0);
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
1
tools/testing/selftests/ptrace/.gitignore
vendored
1
tools/testing/selftests/ptrace/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
get_syscall_info
|
||||
peeksiginfo
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
CFLAGS += -iquote../../../../include/uapi -Wall
|
||||
|
||||
TEST_GEN_PROGS := peeksiginfo
|
||||
TEST_GEN_PROGS := get_syscall_info peeksiginfo
|
||||
|
||||
include ../lib.mk
|
||||
|
271
tools/testing/selftests/ptrace/get_syscall_info.c
Normal file
271
tools/testing/selftests/ptrace/get_syscall_info.c
Normal file
@@ -0,0 +1,271 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Check whether PTRACE_GET_SYSCALL_INFO semantics implemented in the kernel
|
||||
* matches userspace expectations.
|
||||
*/
|
||||
|
||||
#include "../kselftest_harness.h"
|
||||
#include <err.h>
|
||||
#include <signal.h>
|
||||
#include <asm/unistd.h>
|
||||
#include "linux/ptrace.h"
|
||||
|
||||
static int
|
||||
kill_tracee(pid_t pid)
|
||||
{
|
||||
if (!pid)
|
||||
return 0;
|
||||
|
||||
int saved_errno = errno;
|
||||
|
||||
int rc = kill(pid, SIGKILL);
|
||||
|
||||
errno = saved_errno;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static long
|
||||
sys_ptrace(int request, pid_t pid, unsigned long addr, unsigned long data)
|
||||
{
|
||||
return syscall(__NR_ptrace, request, pid, addr, data);
|
||||
}
|
||||
|
||||
#define LOG_KILL_TRACEE(fmt, ...) \
|
||||
do { \
|
||||
kill_tracee(pid); \
|
||||
TH_LOG("wait #%d: " fmt, \
|
||||
ptrace_stop, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
TEST(get_syscall_info)
|
||||
{
|
||||
static const unsigned long args[][7] = {
|
||||
/* a sequence of architecture-agnostic syscalls */
|
||||
{
|
||||
__NR_chdir,
|
||||
(unsigned long) "",
|
||||
0xbad1fed1,
|
||||
0xbad2fed2,
|
||||
0xbad3fed3,
|
||||
0xbad4fed4,
|
||||
0xbad5fed5
|
||||
},
|
||||
{
|
||||
__NR_gettid,
|
||||
0xcaf0bea0,
|
||||
0xcaf1bea1,
|
||||
0xcaf2bea2,
|
||||
0xcaf3bea3,
|
||||
0xcaf4bea4,
|
||||
0xcaf5bea5
|
||||
},
|
||||
{
|
||||
__NR_exit_group,
|
||||
0,
|
||||
0xfac1c0d1,
|
||||
0xfac2c0d2,
|
||||
0xfac3c0d3,
|
||||
0xfac4c0d4,
|
||||
0xfac5c0d5
|
||||
}
|
||||
};
|
||||
const unsigned long *exp_args;
|
||||
|
||||
pid_t pid = fork();
|
||||
|
||||
ASSERT_LE(0, pid) {
|
||||
TH_LOG("fork: %m");
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
/* get the pid before PTRACE_TRACEME */
|
||||
pid = getpid();
|
||||
ASSERT_EQ(0, sys_ptrace(PTRACE_TRACEME, 0, 0, 0)) {
|
||||
TH_LOG("PTRACE_TRACEME: %m");
|
||||
}
|
||||
ASSERT_EQ(0, kill(pid, SIGSTOP)) {
|
||||
/* cannot happen */
|
||||
TH_LOG("kill SIGSTOP: %m");
|
||||
}
|
||||
for (unsigned int i = 0; i < ARRAY_SIZE(args); ++i) {
|
||||
syscall(args[i][0],
|
||||
args[i][1], args[i][2], args[i][3],
|
||||
args[i][4], args[i][5], args[i][6]);
|
||||
}
|
||||
/* unreachable */
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
const struct {
|
||||
unsigned int is_error;
|
||||
int rval;
|
||||
} *exp_param, exit_param[] = {
|
||||
{ 1, -ENOENT }, /* chdir */
|
||||
{ 0, pid } /* gettid */
|
||||
};
|
||||
|
||||
unsigned int ptrace_stop;
|
||||
|
||||
for (ptrace_stop = 0; ; ++ptrace_stop) {
|
||||
struct ptrace_syscall_info info = {
|
||||
.op = 0xff /* invalid PTRACE_SYSCALL_INFO_* op */
|
||||
};
|
||||
const size_t size = sizeof(info);
|
||||
const int expected_none_size =
|
||||
(void *) &info.entry - (void *) &info;
|
||||
const int expected_entry_size =
|
||||
(void *) &info.entry.args[6] - (void *) &info;
|
||||
const int expected_exit_size =
|
||||
(void *) (&info.exit.is_error + 1) -
|
||||
(void *) &info;
|
||||
int status;
|
||||
long rc;
|
||||
|
||||
ASSERT_EQ(pid, wait(&status)) {
|
||||
/* cannot happen */
|
||||
LOG_KILL_TRACEE("wait: %m");
|
||||
}
|
||||
if (WIFEXITED(status)) {
|
||||
pid = 0; /* the tracee is no more */
|
||||
ASSERT_EQ(0, WEXITSTATUS(status));
|
||||
break;
|
||||
}
|
||||
ASSERT_FALSE(WIFSIGNALED(status)) {
|
||||
pid = 0; /* the tracee is no more */
|
||||
LOG_KILL_TRACEE("unexpected signal %u",
|
||||
WTERMSIG(status));
|
||||
}
|
||||
ASSERT_TRUE(WIFSTOPPED(status)) {
|
||||
/* cannot happen */
|
||||
LOG_KILL_TRACEE("unexpected wait status %#x", status);
|
||||
}
|
||||
|
||||
switch (WSTOPSIG(status)) {
|
||||
case SIGSTOP:
|
||||
ASSERT_EQ(0, ptrace_stop) {
|
||||
LOG_KILL_TRACEE("unexpected signal stop");
|
||||
}
|
||||
ASSERT_EQ(0, sys_ptrace(PTRACE_SETOPTIONS, pid, 0,
|
||||
PTRACE_O_TRACESYSGOOD)) {
|
||||
LOG_KILL_TRACEE("PTRACE_SETOPTIONS: %m");
|
||||
}
|
||||
ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
|
||||
pid, size,
|
||||
(unsigned long) &info))) {
|
||||
LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
|
||||
}
|
||||
ASSERT_EQ(expected_none_size, rc) {
|
||||
LOG_KILL_TRACEE("signal stop mismatch");
|
||||
}
|
||||
ASSERT_EQ(PTRACE_SYSCALL_INFO_NONE, info.op) {
|
||||
LOG_KILL_TRACEE("signal stop mismatch");
|
||||
}
|
||||
ASSERT_TRUE(info.arch) {
|
||||
LOG_KILL_TRACEE("signal stop mismatch");
|
||||
}
|
||||
ASSERT_TRUE(info.instruction_pointer) {
|
||||
LOG_KILL_TRACEE("signal stop mismatch");
|
||||
}
|
||||
ASSERT_TRUE(info.stack_pointer) {
|
||||
LOG_KILL_TRACEE("signal stop mismatch");
|
||||
}
|
||||
break;
|
||||
|
||||
case SIGTRAP | 0x80:
|
||||
ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
|
||||
pid, size,
|
||||
(unsigned long) &info))) {
|
||||
LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
|
||||
}
|
||||
switch (ptrace_stop) {
|
||||
case 1: /* entering chdir */
|
||||
case 3: /* entering gettid */
|
||||
case 5: /* entering exit_group */
|
||||
exp_args = args[ptrace_stop / 2];
|
||||
ASSERT_EQ(expected_entry_size, rc) {
|
||||
LOG_KILL_TRACEE("entry stop mismatch");
|
||||
}
|
||||
ASSERT_EQ(PTRACE_SYSCALL_INFO_ENTRY, info.op) {
|
||||
LOG_KILL_TRACEE("entry stop mismatch");
|
||||
}
|
||||
ASSERT_TRUE(info.arch) {
|
||||
LOG_KILL_TRACEE("entry stop mismatch");
|
||||
}
|
||||
ASSERT_TRUE(info.instruction_pointer) {
|
||||
LOG_KILL_TRACEE("entry stop mismatch");
|
||||
}
|
||||
ASSERT_TRUE(info.stack_pointer) {
|
||||
LOG_KILL_TRACEE("entry stop mismatch");
|
||||
}
|
||||
ASSERT_EQ(exp_args[0], info.entry.nr) {
|
||||
LOG_KILL_TRACEE("entry stop mismatch");
|
||||
}
|
||||
ASSERT_EQ(exp_args[1], info.entry.args[0]) {
|
||||
LOG_KILL_TRACEE("entry stop mismatch");
|
||||
}
|
||||
ASSERT_EQ(exp_args[2], info.entry.args[1]) {
|
||||
LOG_KILL_TRACEE("entry stop mismatch");
|
||||
}
|
||||
ASSERT_EQ(exp_args[3], info.entry.args[2]) {
|
||||
LOG_KILL_TRACEE("entry stop mismatch");
|
||||
}
|
||||
ASSERT_EQ(exp_args[4], info.entry.args[3]) {
|
||||
LOG_KILL_TRACEE("entry stop mismatch");
|
||||
}
|
||||
ASSERT_EQ(exp_args[5], info.entry.args[4]) {
|
||||
LOG_KILL_TRACEE("entry stop mismatch");
|
||||
}
|
||||
ASSERT_EQ(exp_args[6], info.entry.args[5]) {
|
||||
LOG_KILL_TRACEE("entry stop mismatch");
|
||||
}
|
||||
break;
|
||||
case 2: /* exiting chdir */
|
||||
case 4: /* exiting gettid */
|
||||
exp_param = &exit_param[ptrace_stop / 2 - 1];
|
||||
ASSERT_EQ(expected_exit_size, rc) {
|
||||
LOG_KILL_TRACEE("exit stop mismatch");
|
||||
}
|
||||
ASSERT_EQ(PTRACE_SYSCALL_INFO_EXIT, info.op) {
|
||||
LOG_KILL_TRACEE("exit stop mismatch");
|
||||
}
|
||||
ASSERT_TRUE(info.arch) {
|
||||
LOG_KILL_TRACEE("exit stop mismatch");
|
||||
}
|
||||
ASSERT_TRUE(info.instruction_pointer) {
|
||||
LOG_KILL_TRACEE("exit stop mismatch");
|
||||
}
|
||||
ASSERT_TRUE(info.stack_pointer) {
|
||||
LOG_KILL_TRACEE("exit stop mismatch");
|
||||
}
|
||||
ASSERT_EQ(exp_param->is_error,
|
||||
info.exit.is_error) {
|
||||
LOG_KILL_TRACEE("exit stop mismatch");
|
||||
}
|
||||
ASSERT_EQ(exp_param->rval, info.exit.rval) {
|
||||
LOG_KILL_TRACEE("exit stop mismatch");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_KILL_TRACEE("unexpected syscall stop");
|
||||
abort();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_KILL_TRACEE("unexpected stop signal %#x",
|
||||
WSTOPSIG(status));
|
||||
abort();
|
||||
}
|
||||
|
||||
ASSERT_EQ(0, sys_ptrace(PTRACE_SYSCALL, pid, 0, 0)) {
|
||||
LOG_KILL_TRACEE("PTRACE_SYSCALL: %m");
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_EQ(ARRAY_SIZE(args) * 2, ptrace_stop);
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
@@ -1775,13 +1775,18 @@ void tracer_ptrace(struct __test_metadata *_metadata, pid_t tracee,
|
||||
unsigned long msg;
|
||||
static bool entry;
|
||||
|
||||
/* Make sure we got an empty message. */
|
||||
/*
|
||||
* The traditional way to tell PTRACE_SYSCALL entry/exit
|
||||
* is by counting.
|
||||
*/
|
||||
entry = !entry;
|
||||
|
||||
/* Make sure we got an appropriate message. */
|
||||
ret = ptrace(PTRACE_GETEVENTMSG, tracee, NULL, &msg);
|
||||
EXPECT_EQ(0, ret);
|
||||
EXPECT_EQ(0, msg);
|
||||
EXPECT_EQ(entry ? PTRACE_EVENTMSG_SYSCALL_ENTRY
|
||||
: PTRACE_EVENTMSG_SYSCALL_EXIT, msg);
|
||||
|
||||
/* The only way to tell PTRACE_SYSCALL entry/exit is by counting. */
|
||||
entry = !entry;
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
|
Reference in New Issue
Block a user