1/*
2 * Copyright (c) International Business Machines  Corp., 2001
3 *
4 * This program is free software;  you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY;  without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
12 * the GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program;  if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19/*
20 * DESCRIPTION
21 *
22 * 1) shmat() chooses a suitable (unused) address when shmaddr is NULL.
23 * 2) shmat() attaches shm segment to the shmaddr when shmaddr is a
24 *    page-aligned address.
25 * 3) shmat() attaches shm segment to the address equal to shmaddr rounded
26 *    down to the nearest multiple of SHMLBA when shmaddr is a page-unaligned
27 *    address and shmflg is set to SHM_RND.
28 * 4) shmat() attaches shm segment to the shmaddr for reading when shmflg
29 *    is set to SHM_RDONLY.
30 */
31
32#include <errno.h>
33#include <sys/types.h>
34#include <sys/ipc.h>
35#include <sys/shm.h>
36#include <sys/wait.h>
37#include <stdlib.h>
38#include <stdint.h>
39
40#include "tst_test.h"
41#include "tst_safe_sysv_ipc.h"
42#include "libnewipc.h"
43
44#define ALIGN_DOWN(in_addr) ((void *)(((uintptr_t)in_addr / SHMLBA) * SHMLBA))
45
46static int shm_id = -1;
47static key_t shm_key;
48static void *null_addr;
49static void *aligned_addr;
50static void *unaligned_addr;
51
52static struct test_case_t {
53	void **shmaddr;
54	int flag;
55	int exp_status;
56	char *desp;
57} tcases[] = {
58	{&null_addr, 0, 0, "NULL address"},
59	{&aligned_addr, 0, 0, "aligned address"},
60	{&unaligned_addr, SHM_RND, 0, "unaligned address with SHM_RND"},
61	{&aligned_addr, SHM_RDONLY, SIGSEGV,
62	"aligned address with SHM_READONLY, and got SIGSEGV on write"}
63};
64
65static void *expected_addr(void *in_addr, void *out_addr)
66{
67	if (!in_addr)
68		return out_addr;
69
70	return ALIGN_DOWN(in_addr);
71}
72
73static void do_child(int *in_addr, int expect_crash)
74{
75	if (expect_crash) {
76		/*
77		 * Crash is expected, avoid dumping corefile.
78		 * 1 is a special value, that disables core-to-pipe.
79		 * At the same time it is small enough value for
80		 * core-to-file, so it skips creating cores as well.
81		*/
82		struct rlimit r;
83
84		r.rlim_cur = 1;
85		r.rlim_max = 1;
86		SAFE_SETRLIMIT(RLIMIT_CORE, &r);
87	}
88	*in_addr = 10;
89
90	exit(0);
91}
92
93static int expected_status(int status, int exp_status)
94{
95	if (!exp_status && WIFEXITED(status))
96		return 0;
97
98	if (exp_status && WIFSIGNALED(status) && WTERMSIG(status) == exp_status)
99		return 0;
100
101	return 1;
102}
103
104static void verify_shmat(unsigned int n)
105{
106	int *addr;
107	pid_t pid;
108	int status;
109	struct shmid_ds buf;
110
111	struct test_case_t *tc = &tcases[n];
112
113	addr = shmat(shm_id, *tc->shmaddr, tc->flag);
114	if (addr == (void *)-1) {
115		tst_res(TFAIL | TERRNO, "shmat() failed");
116		return;
117	}
118
119	SAFE_SHMCTL(shm_id, IPC_STAT, &buf);
120
121	if (buf.shm_nattch != 1) {
122		tst_res(TFAIL, "number of attaches was incorrect");
123		goto end;
124	}
125
126	if (buf.shm_segsz != INT_SIZE) {
127		tst_res(TFAIL, "segment size was incorrect");
128		goto end;
129	}
130
131	if (expected_addr(*tc->shmaddr, addr) != addr) {
132		tst_res(TFAIL,
133			"shared memory address %p is not correct, expected %p",
134			addr, expected_addr(*tc->shmaddr, addr));
135		goto end;
136	}
137
138	pid = SAFE_FORK();
139	if (!pid)
140		do_child(addr, tc->exp_status == SIGSEGV);
141	else
142		SAFE_WAITPID(pid, &status, 0);
143
144	if (expected_status(status, tc->exp_status))
145		tst_res(TFAIL, "shmat() failed to attach %s", tc->desp);
146	else
147		tst_res(TPASS, "shmat() succeeded to attach %s", tc->desp);
148
149end:
150	SAFE_SHMDT(addr);
151}
152
153static void setup(void)
154{
155	aligned_addr = PROBE_FREE_ADDR();
156	unaligned_addr = aligned_addr + SHMLBA - 1;
157
158	shm_key = GETIPCKEY();
159
160	shm_id = SAFE_SHMGET(shm_key, INT_SIZE, SHM_RW | IPC_CREAT | IPC_EXCL);
161}
162
163static void cleanup(void)
164{
165	if (shm_id != -1)
166		SAFE_SHMCTL(shm_id, IPC_RMID, NULL);
167}
168
169static struct tst_test test = {
170	.needs_root = 1,
171	.forks_child = 1,
172	.setup = setup,
173	.cleanup = cleanup,
174	.test = verify_shmat,
175	.tcnt = ARRAY_SIZE(tcases)
176};
177