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