1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 #include <stdlib.h> 4 #include <stdio.h> 5 #include <string.h> 6 #include <errno.h> 7 #include <sys/msg.h> 8 #include <fcntl.h> 9 10 #include "../kselftest.h" 11 12 #define MAX_MSG_SIZE 32 13 14 struct msg1 { 15 int msize; 16 long mtype; 17 char mtext[MAX_MSG_SIZE]; 18 }; 19 20 #define TEST_STRING "Test sysv5 msg" 21 #define MSG_TYPE 1 22 23 #define ANOTHER_TEST_STRING "Yet another test sysv5 msg" 24 #define ANOTHER_MSG_TYPE 26538 25 26 struct msgque_data { 27 key_t key; 28 int msq_id; 29 int qbytes; 30 int qnum; 31 int mode; 32 struct msg1 *messages; 33 }; 34 35 int restore_queue(struct msgque_data *msgque) 36 { 37 int fd, ret, id, i; 38 char buf[32]; 39 40 fd = open("/proc/sys/kernel/msg_next_id", O_WRONLY); 41 if (fd == -1) { 42 printf("Failed to open /proc/sys/kernel/msg_next_id\n"); 43 return -errno; 44 } 45 sprintf(buf, "%d", msgque->msq_id); 46 47 ret = write(fd, buf, strlen(buf)); 48 if (ret != strlen(buf)) { 49 printf("Failed to write to /proc/sys/kernel/msg_next_id\n"); 50 return -errno; 51 } 52 53 id = msgget(msgque->key, msgque->mode | IPC_CREAT | IPC_EXCL); 54 if (id == -1) { 55 printf("Failed to create queue\n"); 56 return -errno; 57 } 58 59 if (id != msgque->msq_id) { 60 printf("Restored queue has wrong id (%d instead of %d)\n", 61 id, msgque->msq_id); 62 ret = -EFAULT; 63 goto destroy; 64 } 65 66 for (i = 0; i < msgque->qnum; i++) { 67 if (msgsnd(msgque->msq_id, &msgque->messages[i].mtype, 68 msgque->messages[i].msize, IPC_NOWAIT) != 0) { 69 printf("msgsnd failed (%m)\n"); 70 ret = -errno; 71 goto destroy; 72 } 73 } 74 return 0; 75 76 destroy: 77 if (msgctl(id, IPC_RMID, NULL)) 78 printf("Failed to destroy queue: %d\n", -errno); 79 return ret; 80 } 81 82 int check_and_destroy_queue(struct msgque_data *msgque) 83 { 84 struct msg1 message; 85 int cnt = 0, ret; 86 87 while (1) { 88 ret = msgrcv(msgque->msq_id, &message.mtype, MAX_MSG_SIZE, 89 0, IPC_NOWAIT); 90 if (ret < 0) { 91 if (errno == ENOMSG) 92 break; 93 printf("Failed to read IPC message: %m\n"); 94 ret = -errno; 95 goto err; 96 } 97 if (ret != msgque->messages[cnt].msize) { 98 printf("Wrong message size: %d (expected %d)\n", ret, 99 msgque->messages[cnt].msize); 100 ret = -EINVAL; 101 goto err; 102 } 103 if (message.mtype != msgque->messages[cnt].mtype) { 104 printf("Wrong message type\n"); 105 ret = -EINVAL; 106 goto err; 107 } 108 if (memcmp(message.mtext, msgque->messages[cnt].mtext, ret)) { 109 printf("Wrong message content\n"); 110 ret = -EINVAL; 111 goto err; 112 } 113 cnt++; 114 } 115 116 if (cnt != msgque->qnum) { 117 printf("Wrong message number\n"); 118 ret = -EINVAL; 119 goto err; 120 } 121 122 ret = 0; 123 err: 124 if (msgctl(msgque->msq_id, IPC_RMID, NULL)) { 125 printf("Failed to destroy queue: %d\n", -errno); 126 return -errno; 127 } 128 return ret; 129 } 130 131 int dump_queue(struct msgque_data *msgque) 132 { 133 struct msqid_ds ds; 134 int kern_id; 135 int i, ret; 136 137 for (kern_id = 0; kern_id < 256; kern_id++) { 138 ret = msgctl(kern_id, MSG_STAT, &ds); 139 if (ret < 0) { 140 if (errno == EINVAL) 141 continue; 142 printf("Failed to get stats for IPC queue with id %d\n", 143 kern_id); 144 return -errno; 145 } 146 147 if (ret == msgque->msq_id) 148 break; 149 } 150 151 msgque->messages = malloc(sizeof(struct msg1) * ds.msg_qnum); 152 if (msgque->messages == NULL) { 153 printf("Failed to get stats for IPC queue\n"); 154 return -ENOMEM; 155 } 156 157 msgque->qnum = ds.msg_qnum; 158 msgque->mode = ds.msg_perm.mode; 159 msgque->qbytes = ds.msg_qbytes; 160 161 for (i = 0; i < msgque->qnum; i++) { 162 ret = msgrcv(msgque->msq_id, &msgque->messages[i].mtype, 163 MAX_MSG_SIZE, i, IPC_NOWAIT | MSG_COPY); 164 if (ret < 0) { 165 printf("Failed to copy IPC message: %m (%d)\n", errno); 166 return -errno; 167 } 168 msgque->messages[i].msize = ret; 169 } 170 return 0; 171 } 172 173 int fill_msgque(struct msgque_data *msgque) 174 { 175 struct msg1 msgbuf; 176 177 msgbuf.mtype = MSG_TYPE; 178 memcpy(msgbuf.mtext, TEST_STRING, sizeof(TEST_STRING)); 179 if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(TEST_STRING), 180 IPC_NOWAIT) != 0) { 181 printf("First message send failed (%m)\n"); 182 return -errno; 183 } 184 185 msgbuf.mtype = ANOTHER_MSG_TYPE; 186 memcpy(msgbuf.mtext, ANOTHER_TEST_STRING, sizeof(ANOTHER_TEST_STRING)); 187 if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(ANOTHER_TEST_STRING), 188 IPC_NOWAIT) != 0) { 189 printf("Second message send failed (%m)\n"); 190 return -errno; 191 } 192 return 0; 193 } 194 195 int main(int argc, char **argv) 196 { 197 int msg, pid, err; 198 struct msgque_data msgque; 199 200 if (getuid() != 0) 201 return ksft_exit_skip( 202 "Please run the test as root - Exiting.\n"); 203 204 msgque.key = ftok(argv[0], 822155650); 205 if (msgque.key == -1) { 206 printf("Can't make key: %d\n", -errno); 207 return ksft_exit_fail(); 208 } 209 210 msgque.msq_id = msgget(msgque.key, IPC_CREAT | IPC_EXCL | 0666); 211 if (msgque.msq_id == -1) { 212 err = -errno; 213 printf("Can't create queue: %d\n", err); 214 goto err_out; 215 } 216 217 err = fill_msgque(&msgque); 218 if (err) { 219 printf("Failed to fill queue: %d\n", err); 220 goto err_destroy; 221 } 222 223 err = dump_queue(&msgque); 224 if (err) { 225 printf("Failed to dump queue: %d\n", err); 226 goto err_destroy; 227 } 228 229 err = check_and_destroy_queue(&msgque); 230 if (err) { 231 printf("Failed to check and destroy queue: %d\n", err); 232 goto err_out; 233 } 234 235 err = restore_queue(&msgque); 236 if (err) { 237 printf("Failed to restore queue: %d\n", err); 238 goto err_destroy; 239 } 240 241 err = check_and_destroy_queue(&msgque); 242 if (err) { 243 printf("Failed to test queue: %d\n", err); 244 goto err_out; 245 } 246 return ksft_exit_pass(); 247 248 err_destroy: 249 if (msgctl(msgque.msq_id, IPC_RMID, NULL)) { 250 printf("Failed to destroy queue: %d\n", -errno); 251 return ksft_exit_fail(); 252 } 253 err_out: 254 return ksft_exit_fail(); 255 } 256