semctl(): separate all layout-dependent copyin/copyout

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Al Viro 45a4a64a 46939168

+97 -101
+97 -101
ipc/sem.c
··· 1177 1177 return res; 1178 1178 } 1179 1179 1180 - static int semctl_nolock(struct ipc_namespace *ns, int semid, 1181 - int cmd, int version, void __user *p) 1180 + static int semctl_stat(struct ipc_namespace *ns, int semid, 1181 + int cmd, struct semid64_ds *semid64) 1182 1182 { 1183 - int err; 1184 1183 struct sem_array *sma; 1184 + int id = 0; 1185 + int err; 1185 1186 1186 - switch (cmd) { 1187 - case IPC_INFO: 1188 - case SEM_INFO: 1189 - { 1190 - struct seminfo seminfo; 1191 - int max_id; 1187 + memset(semid64, 0, sizeof(*semid64)); 1192 1188 1193 - err = security_sem_semctl(NULL, cmd); 1194 - if (err) 1195 - return err; 1196 - 1197 - memset(&seminfo, 0, sizeof(seminfo)); 1198 - seminfo.semmni = ns->sc_semmni; 1199 - seminfo.semmns = ns->sc_semmns; 1200 - seminfo.semmsl = ns->sc_semmsl; 1201 - seminfo.semopm = ns->sc_semopm; 1202 - seminfo.semvmx = SEMVMX; 1203 - seminfo.semmnu = SEMMNU; 1204 - seminfo.semmap = SEMMAP; 1205 - seminfo.semume = SEMUME; 1206 - down_read(&sem_ids(ns).rwsem); 1207 - if (cmd == SEM_INFO) { 1208 - seminfo.semusz = sem_ids(ns).in_use; 1209 - seminfo.semaem = ns->used_sems; 1210 - } else { 1211 - seminfo.semusz = SEMUSZ; 1212 - seminfo.semaem = SEMAEM; 1213 - } 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 - } 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 - 1228 - rcu_read_lock(); 1229 - if (cmd == SEM_STAT) { 1230 - sma = sem_obtain_object(ns, semid); 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 - 1244 - err = -EACCES; 1245 - if (ipcperms(ns, &sma->sem_perm, S_IRUGO)) 1189 + rcu_read_lock(); 1190 + if (cmd == SEM_STAT) { 1191 + sma = sem_obtain_object(ns, semid); 1192 + if (IS_ERR(sma)) { 1193 + err = PTR_ERR(sma); 1246 1194 goto out_unlock; 1247 - 1248 - err = security_sem_semctl(sma, cmd); 1249 - if (err) 1195 + } 1196 + id = sma->sem_perm.id; 1197 + } else { 1198 + sma = sem_obtain_object_check(ns, semid); 1199 + if (IS_ERR(sma)) { 1200 + err = PTR_ERR(sma); 1250 1201 goto out_unlock; 1202 + } 1203 + } 1251 1204 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 - } 1205 + err = -EACCES; 1206 + if (ipcperms(ns, &sma->sem_perm, S_IRUGO)) 1207 + goto out_unlock; 1208 + 1209 + err = security_sem_semctl(sma, cmd); 1210 + if (err) 1211 + goto out_unlock; 1212 + 1213 + kernel_to_ipc64_perm(&sma->sem_perm, &semid64->sem_perm); 1214 + semid64->sem_otime = get_semotime(sma); 1215 + semid64->sem_ctime = sma->sem_ctime; 1216 + semid64->sem_nsems = sma->sem_nsems; 1217 + rcu_read_unlock(); 1218 + return id; 1219 + 1264 1220 out_unlock: 1265 1221 rcu_read_unlock(); 1266 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 1260 static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum, 1270 - unsigned long arg) 1261 + int val) 1271 1262 { 1272 1263 struct sem_undo *un; 1273 1264 struct sem_array *sma; 1274 1265 struct sem *curr; 1275 - int err, val; 1266 + int err; 1276 1267 DEFINE_WAKE_Q(wake_q); 1277 - 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 1268 1286 1269 if (val > SEMVMX || val < 0) 1287 1270 return -ERANGE; ··· 1514 1531 * NOTE: no locks must be held, the rwsem is taken inside this function. 1515 1532 */ 1516 1533 static int semctl_down(struct ipc_namespace *ns, int semid, 1517 - int cmd, int version, void __user *p) 1534 + int cmd, struct semid64_ds *semid64) 1518 1535 { 1519 1536 struct sem_array *sma; 1520 1537 int err; 1521 - struct semid64_ds semid64; 1522 1538 struct kern_ipc_perm *ipcp; 1523 - 1524 - if (cmd == IPC_SET) { 1525 - if (copy_semid_from_user(&semid64, p, version)) 1526 - return -EFAULT; 1527 - } 1528 1539 1529 1540 down_write(&sem_ids(ns).rwsem); 1530 1541 rcu_read_lock(); 1531 1542 1532 1543 ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd, 1533 - &semid64.sem_perm, 0); 1544 + &semid64->sem_perm, 0); 1534 1545 if (IS_ERR(ipcp)) { 1535 1546 err = PTR_ERR(ipcp); 1536 1547 goto out_unlock1; ··· 1544 1567 goto out_up; 1545 1568 case IPC_SET: 1546 1569 sem_lock(sma, NULL, -1); 1547 - err = ipc_update_perm(&semid64.sem_perm, ipcp); 1570 + err = ipc_update_perm(&semid64->sem_perm, ipcp); 1548 1571 if (err) 1549 1572 goto out_unlock0; 1550 1573 sma->sem_ctime = get_seconds(); ··· 1568 1591 int version; 1569 1592 struct ipc_namespace *ns; 1570 1593 void __user *p = (void __user *)arg; 1594 + struct semid64_ds semid64; 1595 + int err; 1571 1596 1572 1597 if (semid < 0) 1573 1598 return -EINVAL; ··· 1580 1601 switch (cmd) { 1581 1602 case IPC_INFO: 1582 1603 case SEM_INFO: 1604 + return semctl_info(ns, semid, cmd, p); 1583 1605 case IPC_STAT: 1584 1606 case SEM_STAT: 1585 - return semctl_nolock(ns, semid, cmd, version, p); 1607 + err = semctl_stat(ns, semid, cmd, &semid64); 1608 + if (err < 0) 1609 + return err; 1610 + if (copy_semid_to_user(p, &semid64, version)) 1611 + err = -EFAULT; 1612 + return err; 1586 1613 case GETALL: 1587 1614 case GETVAL: 1588 1615 case GETPID: ··· 1596 1611 case GETZCNT: 1597 1612 case SETALL: 1598 1613 return semctl_main(ns, semid, semnum, cmd, p); 1599 - case SETVAL: 1600 - return semctl_setval(ns, semid, semnum, arg); 1601 - case IPC_RMID: 1614 + case SETVAL: { 1615 + int val; 1616 + #if defined(CONFIG_64BIT) && defined(__BIG_ENDIAN) 1617 + /* big-endian 64bit */ 1618 + val = arg >> 32; 1619 + #else 1620 + /* 32bit or little-endian 64bit */ 1621 + val = arg; 1622 + #endif 1623 + return semctl_setval(ns, semid, semnum, val); 1624 + } 1602 1625 case IPC_SET: 1603 - return semctl_down(ns, semid, cmd, version, p); 1626 + if (copy_semid_from_user(&semid64, p, version)) 1627 + return -EFAULT; 1628 + case IPC_RMID: 1629 + return semctl_down(ns, semid, cmd, &semid64); 1604 1630 default: 1605 1631 return -EINVAL; 1606 1632 }