Skip to content

Commit 42a0cc3

Browse files
thejhebiederm
authored andcommitted
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: [email protected] Fixes: 1da177e ("Linux-2.6.12-rc2") Signed-off-by: Jann Horn <[email protected]> Signed-off-by: Eric W. Biederman <[email protected]>
1 parent 5820f14 commit 42a0cc3

File tree

5 files changed

+119
-110
lines changed

5 files changed

+119
-110
lines changed

arch/alpha/kernel/osf_sys.c

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -530,24 +530,19 @@ SYSCALL_DEFINE4(osf_mount, unsigned long, typenr, const char __user *, path,
530530
SYSCALL_DEFINE1(osf_utsname, char __user *, name)
531531
{
532532
int error;
533+
char tmp[5 * 32];
533534

534535
down_read(&uts_sem);
535-
error = -EFAULT;
536-
if (copy_to_user(name + 0, utsname()->sysname, 32))
537-
goto out;
538-
if (copy_to_user(name + 32, utsname()->nodename, 32))
539-
goto out;
540-
if (copy_to_user(name + 64, utsname()->release, 32))
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;
536+
memcpy(tmp + 0 * 32, utsname()->sysname, 32);
537+
memcpy(tmp + 1 * 32, utsname()->nodename, 32);
538+
memcpy(tmp + 2 * 32, utsname()->release, 32);
539+
memcpy(tmp + 3 * 32, utsname()->version, 32);
540+
memcpy(tmp + 4 * 32, utsname()->machine, 32);
541+
up_read(&uts_sem);
546542

547-
error = 0;
548-
out:
549-
up_read(&uts_sem);
550-
return error;
543+
if (copy_to_user(name, tmp, sizeof(tmp)))
544+
return -EFAULT;
545+
return 0;
551546
}
552547

553548
SYSCALL_DEFINE0(getpagesize)
@@ -567,18 +562,21 @@ SYSCALL_DEFINE2(osf_getdomainname, char __user *, name, int, namelen)
567562
{
568563
int len, err = 0;
569564
char *kname;
565+
char tmp[32];
570566

571-
if (namelen > 32)
567+
if (namelen < 0 || namelen > 32)
572568
namelen = 32;
573569

574570
down_read(&uts_sem);
575571
kname = utsname()->domainname;
576572
len = strnlen(kname, namelen);
577-
if (copy_to_user(name, kname, min(len + 1, namelen)))
578-
err = -EFAULT;
573+
len = min(len + 1, namelen);
574+
memcpy(tmp, kname, len);
579575
up_read(&uts_sem);
580576

581-
return err;
577+
if (copy_to_user(name, tmp, len))
578+
return -EFAULT;
579+
return 0;
582580
}
583581

584582
/*
@@ -739,27 +737,26 @@ SYSCALL_DEFINE3(osf_sysinfo, int, command, char __user *, buf, long, count)
739737
};
740738
unsigned long offset;
741739
const char *res;
742-
long len, err = -EINVAL;
740+
long len;
741+
char tmp[__NEW_UTS_LEN + 1];
743742

744743
offset = command-1;
745744
if (offset >= ARRAY_SIZE(sysinfo_table)) {
746745
/* Digital UNIX has a few unpublished interfaces here */
747746
printk("sysinfo(%d)", command);
748-
goto out;
747+
return -EINVAL;
749748
}
750749

751750
down_read(&uts_sem);
752751
res = sysinfo_table[offset];
753752
len = strlen(res)+1;
754753
if ((unsigned long)len > (unsigned long)count)
755754
len = count;
756-
if (copy_to_user(buf, res, len))
757-
err = -EFAULT;
758-
else
759-
err = 0;
755+
memcpy(tmp, res, len);
760756
up_read(&uts_sem);
761-
out:
762-
return err;
757+
if (copy_to_user(buf, tmp, len))
758+
return -EFAULT;
759+
return 0;
763760
}
764761

765762
SYSCALL_DEFINE5(osf_getsysinfo, unsigned long, op, void __user *, buffer,

arch/sparc/kernel/sys_sparc_32.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -197,23 +197,27 @@ SYSCALL_DEFINE5(rt_sigaction, int, sig,
197197

198198
SYSCALL_DEFINE2(getdomainname, char __user *, name, int, len)
199199
{
200-
int nlen, err;
201-
200+
int nlen, err;
201+
char tmp[__NEW_UTS_LEN + 1];
202+
202203
if (len < 0)
203204
return -EINVAL;
204205

205-
down_read(&uts_sem);
206-
206+
down_read(&uts_sem);
207+
207208
nlen = strlen(utsname()->domainname) + 1;
208209
err = -EINVAL;
209210
if (nlen > len)
210-
goto out;
211+
goto out_unlock;
212+
memcpy(tmp, utsname()->domainname, nlen);
211213

212-
err = -EFAULT;
213-
if (!copy_to_user(name, utsname()->domainname, nlen))
214-
err = 0;
214+
up_read(&uts_sem);
215215

216-
out:
216+
if (copy_to_user(name, tmp, nlen))
217+
return -EFAULT;
218+
return 0;
219+
220+
out_unlock:
217221
up_read(&uts_sem);
218222
return err;
219223
}

arch/sparc/kernel/sys_sparc_64.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -519,23 +519,27 @@ asmlinkage void sparc_breakpoint(struct pt_regs *regs)
519519

520520
SYSCALL_DEFINE2(getdomainname, char __user *, name, int, len)
521521
{
522-
int nlen, err;
522+
int nlen, err;
523+
char tmp[__NEW_UTS_LEN + 1];
523524

524525
if (len < 0)
525526
return -EINVAL;
526527

527-
down_read(&uts_sem);
528-
528+
down_read(&uts_sem);
529+
529530
nlen = strlen(utsname()->domainname) + 1;
530531
err = -EINVAL;
531532
if (nlen > len)
532-
goto out;
533+
goto out_unlock;
534+
memcpy(tmp, utsname()->domainname, nlen);
535+
536+
up_read(&uts_sem);
533537

534-
err = -EFAULT;
535-
if (!copy_to_user(name, utsname()->domainname, nlen))
536-
err = 0;
538+
if (copy_to_user(name, tmp, nlen))
539+
return -EFAULT;
540+
return 0;
537541

538-
out:
542+
out_unlock:
539543
up_read(&uts_sem);
540544
return err;
541545
}

kernel/sys.c

Lines changed: 45 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,18 +1237,19 @@ static int override_release(char __user *release, size_t len)
12371237

12381238
SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
12391239
{
1240-
int errno = 0;
1240+
struct new_utsname tmp;
12411241

12421242
down_read(&uts_sem);
1243-
if (copy_to_user(name, utsname(), sizeof *name))
1244-
errno = -EFAULT;
1243+
memcpy(&tmp, utsname(), sizeof(tmp));
12451244
up_read(&uts_sem);
1245+
if (copy_to_user(name, &tmp, sizeof(tmp)))
1246+
return -EFAULT;
12461247

1247-
if (!errno && override_release(name->release, sizeof(name->release)))
1248-
errno = -EFAULT;
1249-
if (!errno && override_architecture(name))
1250-
errno = -EFAULT;
1251-
return errno;
1248+
if (override_release(name->release, sizeof(name->release)))
1249+
return -EFAULT;
1250+
if (override_architecture(name))
1251+
return -EFAULT;
1252+
return 0;
12521253
}
12531254

12541255
#ifdef __ARCH_WANT_SYS_OLD_UNAME
@@ -1257,55 +1258,46 @@ SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
12571258
*/
12581259
SYSCALL_DEFINE1(uname, struct old_utsname __user *, name)
12591260
{
1260-
int error = 0;
1261+
struct old_utsname tmp;
12611262

12621263
if (!name)
12631264
return -EFAULT;
12641265

12651266
down_read(&uts_sem);
1266-
if (copy_to_user(name, utsname(), sizeof(*name)))
1267-
error = -EFAULT;
1267+
memcpy(&tmp, utsname(), sizeof(tmp));
12681268
up_read(&uts_sem);
1269+
if (copy_to_user(name, &tmp, sizeof(tmp)))
1270+
return -EFAULT;
12691271

1270-
if (!error && override_release(name->release, sizeof(name->release)))
1271-
error = -EFAULT;
1272-
if (!error && override_architecture(name))
1273-
error = -EFAULT;
1274-
return error;
1272+
if (override_release(name->release, sizeof(name->release)))
1273+
return -EFAULT;
1274+
if (override_architecture(name))
1275+
return -EFAULT;
1276+
return 0;
12751277
}
12761278

12771279
SYSCALL_DEFINE1(olduname, struct oldold_utsname __user *, name)
12781280
{
1279-
int error;
1281+
struct oldold_utsname tmp = {};
12801282

12811283
if (!name)
12821284
return -EFAULT;
1283-
if (!access_ok(VERIFY_WRITE, name, sizeof(struct oldold_utsname)))
1284-
return -EFAULT;
12851285

12861286
down_read(&uts_sem);
1287-
error = __copy_to_user(&name->sysname, &utsname()->sysname,
1288-
__OLD_UTS_LEN);
1289-
error |= __put_user(0, name->sysname + __OLD_UTS_LEN);
1290-
error |= __copy_to_user(&name->nodename, &utsname()->nodename,
1291-
__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);
1287+
memcpy(&tmp.sysname, &utsname()->sysname, __OLD_UTS_LEN);
1288+
memcpy(&tmp.nodename, &utsname()->nodename, __OLD_UTS_LEN);
1289+
memcpy(&tmp.release, &utsname()->release, __OLD_UTS_LEN);
1290+
memcpy(&tmp.version, &utsname()->version, __OLD_UTS_LEN);
1291+
memcpy(&tmp.machine, &utsname()->machine, __OLD_UTS_LEN);
13021292
up_read(&uts_sem);
1293+
if (copy_to_user(name, &tmp, sizeof(tmp)))
1294+
return -EFAULT;
13031295

1304-
if (!error && override_architecture(name))
1305-
error = -EFAULT;
1306-
if (!error && override_release(name->release, sizeof(name->release)))
1307-
error = -EFAULT;
1308-
return error ? -EFAULT : 0;
1296+
if (override_architecture(name))
1297+
return -EFAULT;
1298+
if (override_release(name->release, sizeof(name->release)))
1299+
return -EFAULT;
1300+
return 0;
13091301
}
13101302
#endif
13111303

@@ -1319,26 +1311,28 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)
13191311

13201312
if (len < 0 || len > __NEW_UTS_LEN)
13211313
return -EINVAL;
1322-
down_write(&uts_sem);
13231314
errno = -EFAULT;
13241315
if (!copy_from_user(tmp, name, len)) {
1325-
struct new_utsname *u = utsname();
1316+
struct new_utsname *u;
13261317

1318+
down_write(&uts_sem);
1319+
u = utsname();
13271320
memcpy(u->nodename, tmp, len);
13281321
memset(u->nodename + len, 0, sizeof(u->nodename) - len);
13291322
errno = 0;
13301323
uts_proc_notify(UTS_PROC_HOSTNAME);
1324+
up_write(&uts_sem);
13311325
}
1332-
up_write(&uts_sem);
13331326
return errno;
13341327
}
13351328

13361329
#ifdef __ARCH_WANT_SYS_GETHOSTNAME
13371330

13381331
SYSCALL_DEFINE2(gethostname, char __user *, name, int, len)
13391332
{
1340-
int i, errno;
1333+
int i;
13411334
struct new_utsname *u;
1335+
char tmp[__NEW_UTS_LEN + 1];
13421336

13431337
if (len < 0)
13441338
return -EINVAL;
@@ -1347,11 +1341,11 @@ SYSCALL_DEFINE2(gethostname, char __user *, name, int, len)
13471341
i = 1 + strlen(u->nodename);
13481342
if (i > len)
13491343
i = len;
1350-
errno = 0;
1351-
if (copy_to_user(name, u->nodename, i))
1352-
errno = -EFAULT;
1344+
memcpy(tmp, u->nodename, i);
13531345
up_read(&uts_sem);
1354-
return errno;
1346+
if (copy_to_user(name, tmp, i))
1347+
return -EFAULT;
1348+
return 0;
13551349
}
13561350

13571351
#endif
@@ -1370,17 +1364,18 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len)
13701364
if (len < 0 || len > __NEW_UTS_LEN)
13711365
return -EINVAL;
13721366

1373-
down_write(&uts_sem);
13741367
errno = -EFAULT;
13751368
if (!copy_from_user(tmp, name, len)) {
1376-
struct new_utsname *u = utsname();
1369+
struct new_utsname *u;
13771370

1371+
down_write(&uts_sem);
1372+
u = utsname();
13781373
memcpy(u->domainname, tmp, len);
13791374
memset(u->domainname + len, 0, sizeof(u->domainname) - len);
13801375
errno = 0;
13811376
uts_proc_notify(UTS_PROC_DOMAINNAME);
1377+
up_write(&uts_sem);
13821378
}
1383-
up_write(&uts_sem);
13841379
return errno;
13851380
}
13861381

0 commit comments

Comments
 (0)