summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJann Horn <jannh@google.com>2018-06-25 12:34:10 -0400
committerEric W. Biederman <ebiederm@xmission.com>2018-08-11 03:05:53 -0400
commit42a0cc3478584d4d63f68f2f5af021ddbea771fa (patch)
tree8dc33625ea1c8488e01965f159ef3f597eba42cb
parent5820f140edef111a9ea2ef414ab2428b8cb805b1 (diff)
sys: don't hold uts_sem while accessing userspace memory
Holding uts_sem as a writer while accessing userspace memory allows a namespace admin to stall all processes that attempt to take uts_sem. Instead, move data through stack buffers and don't access userspace memory while uts_sem is held. Cc: stable@vger.kernel.org Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Jann Horn <jannh@google.com> Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
-rw-r--r--arch/alpha/kernel/osf_sys.c51
-rw-r--r--arch/sparc/kernel/sys_sparc_32.c22
-rw-r--r--arch/sparc/kernel/sys_sparc_64.c20
-rw-r--r--kernel/sys.c95
-rw-r--r--kernel/utsname_sysctl.c41
5 files changed, 119 insertions, 110 deletions
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index 6e921754c8fc..2a5c768df335 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -530,24 +530,19 @@ SYSCALL_DEFINE4(osf_mount, unsigned long, typenr, const char __user *, path,
530SYSCALL_DEFINE1(osf_utsname, char __user *, name) 530SYSCALL_DEFINE1(osf_utsname, char __user *, name)
531{ 531{
532 int error; 532 int error;
533 char tmp[5 * 32];
533 534
534 down_read(&uts_sem); 535 down_read(&uts_sem);
535 error = -EFAULT; 536 memcpy(tmp + 0 * 32, utsname()->sysname, 32);
536 if (copy_to_user(name + 0, utsname()->sysname, 32)) 537 memcpy(tmp + 1 * 32, utsname()->nodename, 32);
537 goto out; 538 memcpy(tmp + 2 * 32, utsname()->release, 32);
538 if (copy_to_user(name + 32, utsname()->nodename, 32)) 539 memcpy(tmp + 3 * 32, utsname()->version, 32);
539 goto out; 540 memcpy(tmp + 4 * 32, utsname()->machine, 32);
540 if (copy_to_user(name + 64, utsname()->release, 32)) 541 up_read(&uts_sem);
541 goto out;
542 if (copy_to_user(name + 96, utsname()->version, 32))
543 goto out;
544 if (copy_to_user(name + 128, utsname()->machine, 32))
545 goto out;
546 542
547 error = 0; 543 if (copy_to_user(name, tmp, sizeof(tmp)))
548 out: 544 return -EFAULT;
549 up_read(&uts_sem); 545 return 0;
550 return error;
551} 546}
552 547
553SYSCALL_DEFINE0(getpagesize) 548SYSCALL_DEFINE0(getpagesize)
@@ -567,18 +562,21 @@ SYSCALL_DEFINE2(osf_getdomainname, char __user *, name, int, namelen)
567{ 562{
568 int len, err = 0; 563 int len, err = 0;
569 char *kname; 564 char *kname;
565 char tmp[32];
570 566
571 if (namelen > 32) 567 if (namelen < 0 || namelen > 32)
572 namelen = 32; 568 namelen = 32;
573 569
574 down_read(&uts_sem); 570 down_read(&uts_sem);
575 kname = utsname()->domainname; 571 kname = utsname()->domainname;
576 len = strnlen(kname, namelen); 572 len = strnlen(kname, namelen);
577 if (copy_to_user(name, kname, min(len + 1, namelen))) 573 len = min(len + 1, namelen);
578 err = -EFAULT; 574 memcpy(tmp, kname, len);
579 up_read(&uts_sem); 575 up_read(&uts_sem);
580 576
581 return err; 577 if (copy_to_user(name, tmp, len))
578 return -EFAULT;
579 return 0;
582} 580}
583 581
584/* 582/*
@@ -739,13 +737,14 @@ SYSCALL_DEFINE3(osf_sysinfo, int, command, char __user *, buf, long, count)
739 }; 737 };
740 unsigned long offset; 738 unsigned long offset;
741 const char *res; 739 const char *res;
742 long len, err = -EINVAL; 740 long len;
741 char tmp[__NEW_UTS_LEN + 1];
743 742
744 offset = command-1; 743 offset = command-1;
745 if (offset >= ARRAY_SIZE(sysinfo_table)) { 744 if (offset >= ARRAY_SIZE(sysinfo_table)) {
746 /* Digital UNIX has a few unpublished interfaces here */ 745 /* Digital UNIX has a few unpublished interfaces here */
747 printk("sysinfo(%d)", command); 746 printk("sysinfo(%d)", command);
748 goto out; 747 return -EINVAL;
749 } 748 }
750 749
751 down_read(&uts_sem); 750 down_read(&uts_sem);
@@ -753,13 +752,11 @@ SYSCALL_DEFINE3(osf_sysinfo, int, command, char __user *, buf, long, count)
753 len = strlen(res)+1; 752 len = strlen(res)+1;
754 if ((unsigned long)len > (unsigned long)count) 753 if ((unsigned long)len > (unsigned long)count)
755 len = count; 754 len = count;
756 if (copy_to_user(buf, res, len)) 755 memcpy(tmp, res, len);
757 err = -EFAULT;
758 else
759 err = 0;
760 up_read(&uts_sem); 756 up_read(&uts_sem);
761 out: 757 if (copy_to_user(buf, tmp, len))
762 return err; 758 return -EFAULT;
759 return 0;
763} 760}
764 761
765SYSCALL_DEFINE5(osf_getsysinfo, unsigned long, op, void __user *, buffer, 762SYSCALL_DEFINE5(osf_getsysinfo, unsigned long, op, void __user *, buffer,
diff --git a/arch/sparc/kernel/sys_sparc_32.c b/arch/sparc/kernel/sys_sparc_32.c
index 7f3d9c59719a..452e4d080855 100644
--- a/arch/sparc/kernel/sys_sparc_32.c
+++ b/arch/sparc/kernel/sys_sparc_32.c
@@ -197,23 +197,27 @@ SYSCALL_DEFINE5(rt_sigaction, int, sig,
197 197
198SYSCALL_DEFINE2(getdomainname, char __user *, name, int, len) 198SYSCALL_DEFINE2(getdomainname, char __user *, name, int, len)
199{ 199{
200 int nlen, err; 200 int nlen, err;
201 201 char tmp[__NEW_UTS_LEN + 1];
202
202 if (len < 0) 203 if (len < 0)
203 return -EINVAL; 204 return -EINVAL;
204 205
205 down_read(&uts_sem); 206 down_read(&uts_sem);
206 207
207 nlen = strlen(utsname()->domainname) + 1; 208 nlen = strlen(utsname()->domainname) + 1;
208 err = -EINVAL; 209 err = -EINVAL;
209 if (nlen > len) 210 if (nlen > len)
210 goto out; 211 goto out_unlock;
212 memcpy(tmp, utsname()->domainname, nlen);
211 213
212 err = -EFAULT; 214 up_read(&uts_sem);
213 if (!copy_to_user(name, utsname()->domainname, nlen))
214 err = 0;
215 215
216out: 216 if (copy_to_user(name, tmp, nlen))
217 return -EFAULT;
218 return 0;
219
220out_unlock:
217 up_read(&uts_sem); 221 up_read(&uts_sem);
218 return err; 222 return err;
219} 223}
diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c
index 63baa8aa9414..274ed0b9b3e0 100644
--- a/arch/sparc/kernel/sys_sparc_64.c
+++ b/arch/sparc/kernel/sys_sparc_64.c
@@ -519,23 +519,27 @@ asmlinkage void sparc_breakpoint(struct pt_regs *regs)
519 519
520SYSCALL_DEFINE2(getdomainname, char __user *, name, int, len) 520SYSCALL_DEFINE2(getdomainname, char __user *, name, int, len)
521{ 521{
522 int nlen, err; 522 int nlen, err;
523 char tmp[__NEW_UTS_LEN + 1];
523 524
524 if (len < 0) 525 if (len < 0)
525 return -EINVAL; 526 return -EINVAL;
526 527
527 down_read(&uts_sem); 528 down_read(&uts_sem);
528 529
529 nlen = strlen(utsname()->domainname) + 1; 530 nlen = strlen(utsname()->domainname) + 1;
530 err = -EINVAL; 531 err = -EINVAL;
531 if (nlen > len) 532 if (nlen > len)
532 goto out; 533 goto out_unlock;
534 memcpy(tmp, utsname()->domainname, nlen);
535
536 up_read(&uts_sem);
533 537
534 err = -EFAULT; 538 if (copy_to_user(name, tmp, nlen))
535 if (!copy_to_user(name, utsname()->domainname, nlen)) 539 return -EFAULT;
536 err = 0; 540 return 0;
537 541
538out: 542out_unlock:
539 up_read(&uts_sem); 543 up_read(&uts_sem);
540 return err; 544 return err;
541} 545}
diff --git a/kernel/sys.c b/kernel/sys.c
index 38509dc1f77b..69b9a37ecf0d 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1237,18 +1237,19 @@ static int override_release(char __user *release, size_t len)
1237 1237
1238SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) 1238SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
1239{ 1239{
1240 int errno = 0; 1240 struct new_utsname tmp;
1241 1241
1242 down_read(&uts_sem); 1242 down_read(&uts_sem);
1243 if (copy_to_user(name, utsname(), sizeof *name)) 1243 memcpy(&tmp, utsname(), sizeof(tmp));
1244 errno = -EFAULT;
1245 up_read(&uts_sem); 1244 up_read(&uts_sem);
1245 if (copy_to_user(name, &tmp, sizeof(tmp)))
1246 return -EFAULT;
1246 1247
1247 if (!errno && override_release(name->release, sizeof(name->release))) 1248 if (override_release(name->release, sizeof(name->release)))
1248 errno = -EFAULT; 1249 return -EFAULT;
1249 if (!errno && override_architecture(name)) 1250 if (override_architecture(name))
1250 errno = -EFAULT; 1251 return -EFAULT;
1251 return errno; 1252 return 0;
1252} 1253}
1253 1254
1254#ifdef __ARCH_WANT_SYS_OLD_UNAME 1255#ifdef __ARCH_WANT_SYS_OLD_UNAME
@@ -1257,55 +1258,46 @@ SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
1257 */ 1258 */
1258SYSCALL_DEFINE1(uname, struct old_utsname __user *, name) 1259SYSCALL_DEFINE1(uname, struct old_utsname __user *, name)
1259{ 1260{
1260 int error = 0; 1261 struct old_utsname tmp;
1261 1262
1262 if (!name) 1263 if (!name)
1263 return -EFAULT; 1264 return -EFAULT;
1264 1265
1265 down_read(&uts_sem); 1266 down_read(&uts_sem);
1266 if (copy_to_user(name, utsname(), sizeof(*name))) 1267 memcpy(&tmp, utsname(), sizeof(tmp));
1267 error = -EFAULT;
1268 up_read(&uts_sem); 1268 up_read(&uts_sem);
1269 if (copy_to_user(name, &tmp, sizeof(tmp)))
1270 return -EFAULT;
1269 1271
1270 if (!error && override_release(name->release, sizeof(name->release))) 1272 if (override_release(name->release, sizeof(name->release)))
1271 error = -EFAULT; 1273 return -EFAULT;
1272 if (!error && override_architecture(name)) 1274 if (override_architecture(name))
1273 error = -EFAULT; 1275 return -EFAULT;
1274 return error; 1276 return 0;
1275} 1277}
1276 1278
1277SYSCALL_DEFINE1(olduname, struct oldold_utsname __user *, name) 1279SYSCALL_DEFINE1(olduname, struct oldold_utsname __user *, name)
1278{ 1280{
1279 int error; 1281 struct oldold_utsname tmp = {};
1280 1282
1281 if (!name) 1283 if (!name)
1282 return -EFAULT; 1284 return -EFAULT;
1283 if (!access_ok(VERIFY_WRITE, name, sizeof(struct oldold_utsname)))
1284 return -EFAULT;
1285 1285
1286 down_read(&uts_sem); 1286 down_read(&uts_sem);
1287 error = __copy_to_user(&name->sysname, &utsname()->sysname, 1287 memcpy(&tmp.sysname, &utsname()->sysname, __OLD_UTS_LEN);
1288 __OLD_UTS_LEN); 1288 memcpy(&tmp.nodename, &utsname()->nodename, __OLD_UTS_LEN);
1289 error |= __put_user(0, name->sysname + __OLD_UTS_LEN); 1289 memcpy(&tmp.release, &utsname()->release, __OLD_UTS_LEN);
1290 error |= __copy_to_user(&name->nodename, &utsname()->nodename, 1290 memcpy(&tmp.version, &utsname()->version, __OLD_UTS_LEN);
1291 __OLD_UTS_LEN); 1291 memcpy(&tmp.machine, &utsname()->machine, __OLD_UTS_LEN);
1292 error |= __put_user(0, name->nodename + __OLD_UTS_LEN);
1293 error |= __copy_to_user(&name->release, &utsname()->release,
1294 __OLD_UTS_LEN);
1295 error |= __put_user(0, name->release + __OLD_UTS_LEN);
1296 error |= __copy_to_user(&name->version, &utsname()->version,
1297 __OLD_UTS_LEN);
1298 error |= __put_user(0, name->version + __OLD_UTS_LEN);
1299 error |= __copy_to_user(&name->machine, &utsname()->machine,
1300 __OLD_UTS_LEN);
1301 error |= __put_user(0, name->machine + __OLD_UTS_LEN);
1302 up_read(&uts_sem); 1292 up_read(&uts_sem);
1293 if (copy_to_user(name, &tmp, sizeof(tmp)))
1294 return -EFAULT;
1303 1295
1304 if (!error && override_architecture(name)) 1296 if (override_architecture(name))
1305 error = -EFAULT; 1297 return -EFAULT;
1306 if (!error && override_release(name->release, sizeof(name->release))) 1298 if (override_release(name->release, sizeof(name->release)))
1307 error = -EFAULT; 1299 return -EFAULT;
1308 return error ? -EFAULT : 0; 1300 return 0;
1309} 1301}
1310#endif 1302#endif
1311 1303
@@ -1319,17 +1311,18 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)
1319 1311
1320 if (len < 0 || len > __NEW_UTS_LEN) 1312 if (len < 0 || len > __NEW_UTS_LEN)
1321 return -EINVAL; 1313 return -EINVAL;
1322 down_write(&uts_sem);
1323 errno = -EFAULT; 1314 errno = -EFAULT;
1324 if (!copy_from_user(tmp, name, len)) { 1315 if (!copy_from_user(tmp, name, len)) {
1325 struct new_utsname *u = utsname(); 1316 struct new_utsname *u;
1326 1317
1318 down_write(&uts_sem);
1319 u = utsname();
1327 memcpy(u->nodename, tmp, len); 1320 memcpy(u->nodename, tmp, len);
1328 memset(u->nodename + len, 0, sizeof(u->nodename) - len); 1321 memset(u->nodename + len, 0, sizeof(u->nodename) - len);
1329 errno = 0; 1322 errno = 0;
1330 uts_proc_notify(UTS_PROC_HOSTNAME); 1323 uts_proc_notify(UTS_PROC_HOSTNAME);
1324 up_write(&uts_sem);
1331 } 1325 }
1332 up_write(&uts_sem);
1333 return errno; 1326 return errno;
1334} 1327}
1335 1328
@@ -1337,8 +1330,9 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)
1337 1330
1338SYSCALL_DEFINE2(gethostname, char __user *, name, int, len) 1331SYSCALL_DEFINE2(gethostname, char __user *, name, int, len)
1339{ 1332{
1340 int i, errno; 1333 int i;
1341 struct new_utsname *u; 1334 struct new_utsname *u;
1335 char tmp[__NEW_UTS_LEN + 1];
1342 1336
1343 if (len < 0) 1337 if (len < 0)
1344 return -EINVAL; 1338 return -EINVAL;
@@ -1347,11 +1341,11 @@ SYSCALL_DEFINE2(gethostname, char __user *, name, int, len)
1347 i = 1 + strlen(u->nodename); 1341 i = 1 + strlen(u->nodename);
1348 if (i > len) 1342 if (i > len)
1349 i = len; 1343 i = len;
1350 errno = 0; 1344 memcpy(tmp, u->nodename, i);
1351 if (copy_to_user(name, u->nodename, i))
1352 errno = -EFAULT;
1353 up_read(&uts_sem); 1345 up_read(&uts_sem);
1354 return errno; 1346 if (copy_to_user(name, tmp, i))
1347 return -EFAULT;
1348 return 0;
1355} 1349}
1356 1350
1357#endif 1351#endif
@@ -1370,17 +1364,18 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len)
1370 if (len < 0 || len > __NEW_UTS_LEN) 1364 if (len < 0 || len > __NEW_UTS_LEN)
1371 return -EINVAL; 1365 return -EINVAL;
1372 1366
1373 down_write(&uts_sem);
1374 errno = -EFAULT; 1367 errno = -EFAULT;
1375 if (!copy_from_user(tmp, name, len)) { 1368 if (!copy_from_user(tmp, name, len)) {
1376 struct new_utsname *u = utsname(); 1369 struct new_utsname *u;
1377 1370
1371 down_write(&uts_sem);
1372 u = utsname();
1378 memcpy(u->domainname, tmp, len); 1373 memcpy(u->domainname, tmp, len);
1379 memset(u->domainname + len, 0, sizeof(u->domainname) - len); 1374 memset(u->domainname + len, 0, sizeof(u->domainname) - len);
1380 errno = 0; 1375 errno = 0;
1381 uts_proc_notify(UTS_PROC_DOMAINNAME); 1376 uts_proc_notify(UTS_PROC_DOMAINNAME);
1377 up_write(&uts_sem);
1382 } 1378 }
1383 up_write(&uts_sem);
1384 return errno; 1379 return errno;
1385} 1380}
1386 1381
diff --git a/kernel/utsname_sysctl.c b/kernel/utsname_sysctl.c
index 233cd8fc6910..258033d62cb3 100644
--- a/kernel/utsname_sysctl.c
+++ b/kernel/utsname_sysctl.c
@@ -18,7 +18,7 @@
18 18
19#ifdef CONFIG_PROC_SYSCTL 19#ifdef CONFIG_PROC_SYSCTL
20 20
21static void *get_uts(struct ctl_table *table, int write) 21static void *get_uts(struct ctl_table *table)
22{ 22{
23 char *which = table->data; 23 char *which = table->data;
24 struct uts_namespace *uts_ns; 24 struct uts_namespace *uts_ns;
@@ -26,21 +26,9 @@ static void *get_uts(struct ctl_table *table, int write)
26 uts_ns = current->nsproxy->uts_ns; 26 uts_ns = current->nsproxy->uts_ns;
27 which = (which - (char *)&init_uts_ns) + (char *)uts_ns; 27 which = (which - (char *)&init_uts_ns) + (char *)uts_ns;
28 28
29 if (!write)
30 down_read(&uts_sem);
31 else
32 down_write(&uts_sem);
33 return which; 29 return which;
34} 30}
35 31
36static void put_uts(struct ctl_table *table, int write, void *which)
37{
38 if (!write)
39 up_read(&uts_sem);
40 else
41 up_write(&uts_sem);
42}
43
44/* 32/*
45 * Special case of dostring for the UTS structure. This has locks 33 * Special case of dostring for the UTS structure. This has locks
46 * to observe. Should this be in kernel/sys.c ???? 34 * to observe. Should this be in kernel/sys.c ????
@@ -50,13 +38,34 @@ static int proc_do_uts_string(struct ctl_table *table, int write,
50{ 38{
51 struct ctl_table uts_table; 39 struct ctl_table uts_table;
52 int r; 40 int r;
41 char tmp_data[__NEW_UTS_LEN + 1];
42
53 memcpy(&uts_table, table, sizeof(uts_table)); 43 memcpy(&uts_table, table, sizeof(uts_table));
54 uts_table.data = get_uts(table, write); 44 uts_table.data = tmp_data;
45
46 /*
47 * Buffer the value in tmp_data so that proc_dostring() can be called
48 * without holding any locks.
49 * We also need to read the original value in the write==1 case to
50 * support partial writes.
51 */
52 down_read(&uts_sem);
53 memcpy(tmp_data, get_uts(table), sizeof(tmp_data));
54 up_read(&uts_sem);
55 r = proc_dostring(&uts_table, write, buffer, lenp, ppos); 55 r = proc_dostring(&uts_table, write, buffer, lenp, ppos);
56 put_uts(table, write, uts_table.data);
57 56
58 if (write) 57 if (write) {
58 /*
59 * Write back the new value.
60 * Note that, since we dropped uts_sem, the result can
61 * theoretically be incorrect if there are two parallel writes
62 * at non-zero offsets to the same sysctl.
63 */
64 down_write(&uts_sem);
65 memcpy(get_uts(table), tmp_data, sizeof(tmp_data));
66 up_write(&uts_sem);
59 proc_sys_poll_notify(table->poll); 67 proc_sys_poll_notify(table->poll);
68 }
60 69
61 return r; 70 return r;
62} 71}