1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2018 FUJITSU LIMITED. All rights reserved.
4 * Author: Xiao Yang <yangx.jy@cn.fujitsu.com>
5 */
6/*
7 * Description:
8 * Check various errnos for mlock2(2) since kernel v2.6.9:
9 * 1) mlock2() fails and returns EINVAL if unknown flag is specified.
10 * 2) mlock2() fails and returns ENOMEM if the caller is not
11 *    privileged(CAP_IPC_LOCK) and tries to lock more memory than the
12 *    RLIMIT_MEMLOCK limit.
13 * 3) mlock2() fails and returns EPERM if the caller is not
14 *    privileged(CAP_IPC_LOCK) and its RLIMIT_MEMLOCK limit is 0.
15 * 4) mlock2() fails and returns ENOMEM if some of the specified address
16 *    range does not correspond to mapped pages in the address space
17 *    of the caller.
18 */
19#include <errno.h>
20#include <unistd.h>
21#include <sys/mman.h>
22#include <sys/time.h>
23#include <sys/resource.h>
24#include <pwd.h>
25
26#include "tst_test.h"
27#include "lapi/syscalls.h"
28
29#define PAGES 8
30
31static size_t pgsz;
32static size_t max_sz1, max_sz2;
33static char *addr, *unmapped_addr;
34static struct passwd *nobody;
35
36static struct tcase {
37	char **taddr;
38	int flag;
39	size_t *max_size;
40	/* 1: nobody 0: root */
41	int user;
42	int exp_err;
43} tcases[] = {
44	{&addr, -1, NULL, 0, EINVAL},
45	{&addr, 0, &max_sz1, 1, ENOMEM},
46	{&addr, 0, &max_sz2, 1, EPERM},
47	{&unmapped_addr, 0, NULL, 0, ENOMEM},
48};
49
50static void verify_mlock2(unsigned int n)
51{
52	struct tcase *tc = &tcases[n];
53	struct rlimit orig_limit, new_limit;
54
55	if (tc->user) {
56		SAFE_GETRLIMIT(RLIMIT_MEMLOCK, &orig_limit);
57		new_limit.rlim_cur = *tc->max_size;
58		new_limit.rlim_max = *tc->max_size;
59		SAFE_SETRLIMIT(RLIMIT_MEMLOCK, &new_limit);
60		SAFE_SETEUID(nobody->pw_uid);
61	}
62
63	TEST(tst_syscall(__NR_mlock2, *tc->taddr, pgsz, tc->flag));
64	if (TST_RET != -1) {
65		tst_res(TFAIL, "mlock2() succeeded");
66		SAFE_MUNLOCK(*tc->taddr, pgsz);
67		goto end;
68	}
69
70	if (TST_ERR != tc->exp_err) {
71		tst_res(TFAIL | TTERRNO,
72			"mlock2() failed unexpectedly, expected %s",
73			tst_strerrno(tc->exp_err));
74	} else {
75		tst_res(TPASS | TTERRNO, "mlock2() failed as expected");
76	}
77
78end:
79	if (tc->user) {
80		SAFE_SETEUID(0);
81		SAFE_SETRLIMIT(RLIMIT_MEMLOCK, &orig_limit);
82	}
83}
84
85static void setup(void)
86{
87	pgsz = getpagesize();
88	nobody = SAFE_GETPWNAM("nobody");
89
90	addr = SAFE_MMAP(NULL, pgsz, PROT_WRITE,
91			 MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
92	unmapped_addr = SAFE_MMAP(NULL, pgsz * PAGES, PROT_READ,
93				  MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
94	SAFE_MUNMAP(unmapped_addr, pgsz * PAGES);
95	unmapped_addr = unmapped_addr + pgsz * PAGES / 2;
96
97	max_sz1 = pgsz - 1;
98}
99
100static void cleanup(void)
101{
102	if (addr)
103		SAFE_MUNMAP(addr, pgsz);
104}
105
106static struct tst_test test = {
107	.tcnt = ARRAY_SIZE(tcases),
108	.test = verify_mlock2,
109	.setup = setup,
110	.cleanup = cleanup,
111	.needs_root = 1,
112};
113