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 return ksft_exit_skip( 201 "Please run the test as root - Exiting.\n"); 202 203 msgque.key = ftok(argv[0], 822155650); 204 if (msgque.key == -1) { 205 printf("Can't make key: %d\n", -errno); 206 return ksft_exit_fail(); 207 } 208 209 msgque.msq_id = msgget(msgque.key, IPC_CREAT | IPC_EXCL | 0666); 210 if (msgque.msq_id == -1) { 211 err = -errno; 212 printf("Can't create queue: %d\n", err); 213 goto err_out; 214 } 215 216 err = fill_msgque(&msgque); 217 if (err) { 218 printf("Failed to fill queue: %d\n", err); 219 goto err_destroy; 220 } 221 222 err = dump_queue(&msgque); 223 if (err) { 224 printf("Failed to dump queue: %d\n", err); 225 goto err_destroy; 226 } 227 228 err = check_and_destroy_queue(&msgque); 229 if (err) { 230 printf("Failed to check and destroy queue: %d\n", err); 231 goto err_out; 232 } 233 234 err = restore_queue(&msgque); 235 if (err) { 236 printf("Failed to restore queue: %d\n", err); 237 goto err_destroy; 238 } 239 240 err = check_and_destroy_queue(&msgque); 241 if (err) { 242 printf("Failed to test queue: %d\n", err); 243 goto err_out; 244 } 245 return ksft_exit_pass(); 246 247 err_destroy: 248 if (msgctl(msgque.msq_id, IPC_RMID, 0)) { 249 printf("Failed to destroy queue: %d\n", -errno); 250 return ksft_exit_fail(); 251 } 252 err_out: 253 return ksft_exit_fail(); 254 } 255