diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2017-07-09 09:11:00 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2017-07-15 20:46:44 -0400 |
commit | 45a4a64ab485d5c3e76ee79163a24303bf5077fd (patch) | |
tree | 2aa465fe39e8162604afb1435bdbb6731538270d /ipc | |
parent | 4693916846269d633a3664586650dbfac2c5562f (diff) |
semctl(): separate all layout-dependent copyin/copyout
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'ipc')
-rw-r--r-- | ipc/sem.c | 192 |
1 files changed, 94 insertions, 98 deletions
@@ -1177,112 +1177,95 @@ static time_t get_semotime(struct sem_array *sma) | |||
1177 | return res; | 1177 | return res; |
1178 | } | 1178 | } |
1179 | 1179 | ||
1180 | static int semctl_nolock(struct ipc_namespace *ns, int semid, | 1180 | static int semctl_stat(struct ipc_namespace *ns, int semid, |
1181 | int cmd, int version, void __user *p) | 1181 | int cmd, struct semid64_ds *semid64) |
1182 | { | 1182 | { |
1183 | int err; | ||
1184 | struct sem_array *sma; | 1183 | struct sem_array *sma; |
1184 | int id = 0; | ||
1185 | int err; | ||
1185 | 1186 | ||
1186 | switch (cmd) { | 1187 | memset(semid64, 0, sizeof(*semid64)); |
1187 | case IPC_INFO: | ||
1188 | case SEM_INFO: | ||
1189 | { | ||
1190 | struct seminfo seminfo; | ||
1191 | int max_id; | ||
1192 | |||
1193 | err = security_sem_semctl(NULL, cmd); | ||
1194 | if (err) | ||
1195 | return err; | ||
1196 | 1188 | ||
1197 | memset(&seminfo, 0, sizeof(seminfo)); | 1189 | rcu_read_lock(); |
1198 | seminfo.semmni = ns->sc_semmni; | 1190 | if (cmd == SEM_STAT) { |
1199 | seminfo.semmns = ns->sc_semmns; | 1191 | sma = sem_obtain_object(ns, semid); |
1200 | seminfo.semmsl = ns->sc_semmsl; | 1192 | if (IS_ERR(sma)) { |
1201 | seminfo.semopm = ns->sc_semopm; | 1193 | err = PTR_ERR(sma); |
1202 | seminfo.semvmx = SEMVMX; | 1194 | goto out_unlock; |
1203 | seminfo.semmnu = SEMMNU; | 1195 | } |
1204 | seminfo.semmap = SEMMAP; | 1196 | id = sma->sem_perm.id; |
1205 | seminfo.semume = SEMUME; | 1197 | } else { |
1206 | down_read(&sem_ids(ns).rwsem); | 1198 | sma = sem_obtain_object_check(ns, semid); |
1207 | if (cmd == SEM_INFO) { | 1199 | if (IS_ERR(sma)) { |
1208 | seminfo.semusz = sem_ids(ns).in_use; | 1200 | err = PTR_ERR(sma); |
1209 | seminfo.semaem = ns->used_sems; | 1201 | goto out_unlock; |
1210 | } else { | ||
1211 | seminfo.semusz = SEMUSZ; | ||
1212 | seminfo.semaem = SEMAEM; | ||
1213 | } | 1202 | } |
1214 | max_id = ipc_get_maxid(&sem_ids(ns)); | ||
1215 | up_read(&sem_ids(ns).rwsem); | ||
1216 | if (copy_to_user(p, &seminfo, sizeof(struct seminfo))) | ||
1217 | return -EFAULT; | ||
1218 | return (max_id < 0) ? 0 : max_id; | ||
1219 | } | 1203 | } |
1220 | case IPC_STAT: | ||
1221 | case SEM_STAT: | ||
1222 | { | ||
1223 | struct semid64_ds tbuf; | ||
1224 | int id = 0; | ||
1225 | |||
1226 | memset(&tbuf, 0, sizeof(tbuf)); | ||
1227 | 1204 | ||
1228 | rcu_read_lock(); | 1205 | err = -EACCES; |
1229 | if (cmd == SEM_STAT) { | 1206 | if (ipcperms(ns, &sma->sem_perm, S_IRUGO)) |
1230 | sma = sem_obtain_object(ns, semid); | 1207 | goto out_unlock; |
1231 | if (IS_ERR(sma)) { | ||
1232 | err = PTR_ERR(sma); | ||
1233 | goto out_unlock; | ||
1234 | } | ||
1235 | id = sma->sem_perm.id; | ||
1236 | } else { | ||
1237 | sma = sem_obtain_object_check(ns, semid); | ||
1238 | if (IS_ERR(sma)) { | ||
1239 | err = PTR_ERR(sma); | ||
1240 | goto out_unlock; | ||
1241 | } | ||
1242 | } | ||
1243 | 1208 | ||
1244 | err = -EACCES; | 1209 | err = security_sem_semctl(sma, cmd); |
1245 | if (ipcperms(ns, &sma->sem_perm, S_IRUGO)) | 1210 | if (err) |
1246 | goto out_unlock; | 1211 | goto out_unlock; |
1247 | 1212 | ||
1248 | err = security_sem_semctl(sma, cmd); | 1213 | kernel_to_ipc64_perm(&sma->sem_perm, &semid64->sem_perm); |
1249 | if (err) | 1214 | semid64->sem_otime = get_semotime(sma); |
1250 | goto out_unlock; | 1215 | semid64->sem_ctime = sma->sem_ctime; |
1216 | semid64->sem_nsems = sma->sem_nsems; | ||
1217 | rcu_read_unlock(); | ||
1218 | return id; | ||
1251 | 1219 | ||
1252 | kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm); | ||
1253 | tbuf.sem_otime = get_semotime(sma); | ||
1254 | tbuf.sem_ctime = sma->sem_ctime; | ||
1255 | tbuf.sem_nsems = sma->sem_nsems; | ||
1256 | rcu_read_unlock(); | ||
1257 | if (copy_semid_to_user(p, &tbuf, version)) | ||
1258 | return -EFAULT; | ||
1259 | return id; | ||
1260 | } | ||
1261 | default: | ||
1262 | return -EINVAL; | ||
1263 | } | ||
1264 | out_unlock: | 1220 | out_unlock: |
1265 | rcu_read_unlock(); | 1221 | rcu_read_unlock(); |
1266 | return err; | 1222 | return err; |
1267 | } | 1223 | } |
1268 | 1224 | ||
1225 | static int semctl_info(struct ipc_namespace *ns, int semid, | ||
1226 | int cmd, void __user *p) | ||
1227 | { | ||
1228 | struct seminfo seminfo; | ||
1229 | int max_id; | ||
1230 | int err; | ||
1231 | |||
1232 | err = security_sem_semctl(NULL, cmd); | ||
1233 | if (err) | ||
1234 | return err; | ||
1235 | |||
1236 | memset(&seminfo, 0, sizeof(seminfo)); | ||
1237 | seminfo.semmni = ns->sc_semmni; | ||
1238 | seminfo.semmns = ns->sc_semmns; | ||
1239 | seminfo.semmsl = ns->sc_semmsl; | ||
1240 | seminfo.semopm = ns->sc_semopm; | ||
1241 | seminfo.semvmx = SEMVMX; | ||
1242 | seminfo.semmnu = SEMMNU; | ||
1243 | seminfo.semmap = SEMMAP; | ||
1244 | seminfo.semume = SEMUME; | ||
1245 | down_read(&sem_ids(ns).rwsem); | ||
1246 | if (cmd == SEM_INFO) { | ||
1247 | seminfo.semusz = sem_ids(ns).in_use; | ||
1248 | seminfo.semaem = ns->used_sems; | ||
1249 | } else { | ||
1250 | seminfo.semusz = SEMUSZ; | ||
1251 | seminfo.semaem = SEMAEM; | ||
1252 | } | ||
1253 | max_id = ipc_get_maxid(&sem_ids(ns)); | ||
1254 | up_read(&sem_ids(ns).rwsem); | ||
1255 | if (copy_to_user(p, &seminfo, sizeof(struct seminfo))) | ||
1256 | return -EFAULT; | ||
1257 | return (max_id < 0) ? 0 : max_id; | ||
1258 | } | ||
1259 | |||
1269 | static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum, | 1260 | static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum, |
1270 | unsigned long arg) | 1261 | int val) |
1271 | { | 1262 | { |
1272 | struct sem_undo *un; | 1263 | struct sem_undo *un; |
1273 | struct sem_array *sma; | 1264 | struct sem_array *sma; |
1274 | struct sem *curr; | 1265 | struct sem *curr; |
1275 | int err, val; | 1266 | int err; |
1276 | DEFINE_WAKE_Q(wake_q); | 1267 | DEFINE_WAKE_Q(wake_q); |
1277 | 1268 | ||
1278 | #if defined(CONFIG_64BIT) && defined(__BIG_ENDIAN) | ||
1279 | /* big-endian 64bit */ | ||
1280 | val = arg >> 32; | ||
1281 | #else | ||
1282 | /* 32bit or little-endian 64bit */ | ||
1283 | val = arg; | ||
1284 | #endif | ||
1285 | |||
1286 | if (val > SEMVMX || val < 0) | 1269 | if (val > SEMVMX || val < 0) |
1287 | return -ERANGE; | 1270 | return -ERANGE; |
1288 | 1271 | ||
@@ -1531,23 +1514,17 @@ copy_semid_from_user(struct semid64_ds *out, void __user *buf, int version) | |||
1531 | * NOTE: no locks must be held, the rwsem is taken inside this function. | 1514 | * NOTE: no locks must be held, the rwsem is taken inside this function. |
1532 | */ | 1515 | */ |
1533 | static int semctl_down(struct ipc_namespace *ns, int semid, | 1516 | static int semctl_down(struct ipc_namespace *ns, int semid, |
1534 | int cmd, int version, void __user *p) | 1517 | int cmd, struct semid64_ds *semid64) |
1535 | { | 1518 | { |
1536 | struct sem_array *sma; | 1519 | struct sem_array *sma; |
1537 | int err; | 1520 | int err; |
1538 | struct semid64_ds semid64; | ||
1539 | struct kern_ipc_perm *ipcp; | 1521 | struct kern_ipc_perm *ipcp; |
1540 | 1522 | ||
1541 | if (cmd == IPC_SET) { | ||
1542 | if (copy_semid_from_user(&semid64, p, version)) | ||
1543 | return -EFAULT; | ||
1544 | } | ||
1545 | |||
1546 | down_write(&sem_ids(ns).rwsem); | 1523 | down_write(&sem_ids(ns).rwsem); |
1547 | rcu_read_lock(); | 1524 | rcu_read_lock(); |
1548 | 1525 | ||
1549 | ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd, | 1526 | ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd, |
1550 | &semid64.sem_perm, 0); | 1527 | &semid64->sem_perm, 0); |
1551 | if (IS_ERR(ipcp)) { | 1528 | if (IS_ERR(ipcp)) { |
1552 | err = PTR_ERR(ipcp); | 1529 | err = PTR_ERR(ipcp); |
1553 | goto out_unlock1; | 1530 | goto out_unlock1; |
@@ -1567,7 +1544,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid, | |||
1567 | goto out_up; | 1544 | goto out_up; |
1568 | case IPC_SET: | 1545 | case IPC_SET: |
1569 | sem_lock(sma, NULL, -1); | 1546 | sem_lock(sma, NULL, -1); |
1570 | err = ipc_update_perm(&semid64.sem_perm, ipcp); | 1547 | err = ipc_update_perm(&semid64->sem_perm, ipcp); |
1571 | if (err) | 1548 | if (err) |
1572 | goto out_unlock0; | 1549 | goto out_unlock0; |
1573 | sma->sem_ctime = get_seconds(); | 1550 | sma->sem_ctime = get_seconds(); |
@@ -1591,6 +1568,8 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) | |||
1591 | int version; | 1568 | int version; |
1592 | struct ipc_namespace *ns; | 1569 | struct ipc_namespace *ns; |
1593 | void __user *p = (void __user *)arg; | 1570 | void __user *p = (void __user *)arg; |
1571 | struct semid64_ds semid64; | ||
1572 | int err; | ||
1594 | 1573 | ||
1595 | if (semid < 0) | 1574 | if (semid < 0) |
1596 | return -EINVAL; | 1575 | return -EINVAL; |
@@ -1601,9 +1580,15 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) | |||
1601 | switch (cmd) { | 1580 | switch (cmd) { |
1602 | case IPC_INFO: | 1581 | case IPC_INFO: |
1603 | case SEM_INFO: | 1582 | case SEM_INFO: |
1583 | return semctl_info(ns, semid, cmd, p); | ||
1604 | case IPC_STAT: | 1584 | case IPC_STAT: |
1605 | case SEM_STAT: | 1585 | case SEM_STAT: |
1606 | return semctl_nolock(ns, semid, cmd, version, p); | 1586 | err = semctl_stat(ns, semid, cmd, &semid64); |
1587 | if (err < 0) | ||
1588 | return err; | ||
1589 | if (copy_semid_to_user(p, &semid64, version)) | ||
1590 | err = -EFAULT; | ||
1591 | return err; | ||
1607 | case GETALL: | 1592 | case GETALL: |
1608 | case GETVAL: | 1593 | case GETVAL: |
1609 | case GETPID: | 1594 | case GETPID: |
@@ -1611,11 +1596,22 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) | |||
1611 | case GETZCNT: | 1596 | case GETZCNT: |
1612 | case SETALL: | 1597 | case SETALL: |
1613 | return semctl_main(ns, semid, semnum, cmd, p); | 1598 | return semctl_main(ns, semid, semnum, cmd, p); |
1614 | case SETVAL: | 1599 | case SETVAL: { |
1615 | return semctl_setval(ns, semid, semnum, arg); | 1600 | int val; |
1616 | case IPC_RMID: | 1601 | #if defined(CONFIG_64BIT) && defined(__BIG_ENDIAN) |
1602 | /* big-endian 64bit */ | ||
1603 | val = arg >> 32; | ||
1604 | #else | ||
1605 | /* 32bit or little-endian 64bit */ | ||
1606 | val = arg; | ||
1607 | #endif | ||
1608 | return semctl_setval(ns, semid, semnum, val); | ||
1609 | } | ||
1617 | case IPC_SET: | 1610 | case IPC_SET: |
1618 | return semctl_down(ns, semid, cmd, version, p); | 1611 | if (copy_semid_from_user(&semid64, p, version)) |
1612 | return -EFAULT; | ||
1613 | case IPC_RMID: | ||
1614 | return semctl_down(ns, semid, cmd, &semid64); | ||
1619 | default: | 1615 | default: |
1620 | return -EINVAL; | 1616 | return -EINVAL; |
1621 | } | 1617 | } |