Skip to content

Commit b692c40

Browse files
thejhgregkh
authored andcommitted
sys: don't hold uts_sem while accessing userspace memory
commit 42a0cc3 upstream. 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]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent c2ea292 commit b692c40

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
@@ -204,23 +204,27 @@ SYSCALL_DEFINE5(rt_sigaction, int, sig,
204204

205205
asmlinkage long sys_getdomainname(char __user *name, int len)
206206
{
207-
int nlen, err;
208-
207+
int nlen, err;
208+
char tmp[__NEW_UTS_LEN + 1];
209+
209210
if (len < 0)
210211
return -EINVAL;
211212

212-
down_read(&uts_sem);
213-
213+
down_read(&uts_sem);
214+
214215
nlen = strlen(utsname()->domainname) + 1;
215216
err = -EINVAL;
216217
if (nlen > len)
217-
goto out;
218+
goto out_unlock;
219+
memcpy(tmp, utsname()->domainname, nlen);
218220

219-
err = -EFAULT;
220-
if (!copy_to_user(name, utsname()->domainname, nlen))
221-
err = 0;
221+
up_read(&uts_sem);
222222

223-
out:
223+
if (copy_to_user(name, tmp, nlen))
224+
return -EFAULT;
225+
return 0;
226+
227+
out_unlock:
224228
up_read(&uts_sem);
225229
return err;
226230
}

arch/sparc/kernel/sys_sparc_64.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -527,23 +527,27 @@ extern void check_pending(int signum);
527527

528528
SYSCALL_DEFINE2(getdomainname, char __user *, name, int, len)
529529
{
530-
int nlen, err;
530+
int nlen, err;
531+
char tmp[__NEW_UTS_LEN + 1];
531532

532533
if (len < 0)
533534
return -EINVAL;
534535

535-
down_read(&uts_sem);
536-
536+
down_read(&uts_sem);
537+
537538
nlen = strlen(utsname()->domainname) + 1;
538539
err = -EINVAL;
539540
if (nlen > len)
540-
goto out;
541+
goto out_unlock;
542+
memcpy(tmp, utsname()->domainname, nlen);
543+
544+
up_read(&uts_sem);
541545

542-
err = -EFAULT;
543-
if (!copy_to_user(name, utsname()->domainname, nlen))
544-
err = 0;
546+
if (copy_to_user(name, tmp, nlen))
547+
return -EFAULT;
548+
return 0;
545549

546-
out:
550+
out_unlock:
547551
up_read(&uts_sem);
548552
return err;
549553
}

kernel/sys.c

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

11771177
SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
11781178
{
1179-
int errno = 0;
1179+
struct new_utsname tmp;
11801180

11811181
down_read(&uts_sem);
1182-
if (copy_to_user(name, utsname(), sizeof *name))
1183-
errno = -EFAULT;
1182+
memcpy(&tmp, utsname(), sizeof(tmp));
11841183
up_read(&uts_sem);
1184+
if (copy_to_user(name, &tmp, sizeof(tmp)))
1185+
return -EFAULT;
11851186

1186-
if (!errno && override_release(name->release, sizeof(name->release)))
1187-
errno = -EFAULT;
1188-
if (!errno && override_architecture(name))
1189-
errno = -EFAULT;
1190-
return errno;
1187+
if (override_release(name->release, sizeof(name->release)))
1188+
return -EFAULT;
1189+
if (override_architecture(name))
1190+
return -EFAULT;
1191+
return 0;
11911192
}
11921193

11931194
#ifdef __ARCH_WANT_SYS_OLD_UNAME
@@ -1196,55 +1197,46 @@ SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
11961197
*/
11971198
SYSCALL_DEFINE1(uname, struct old_utsname __user *, name)
11981199
{
1199-
int error = 0;
1200+
struct old_utsname tmp;
12001201

12011202
if (!name)
12021203
return -EFAULT;
12031204

12041205
down_read(&uts_sem);
1205-
if (copy_to_user(name, utsname(), sizeof(*name)))
1206-
error = -EFAULT;
1206+
memcpy(&tmp, utsname(), sizeof(tmp));
12071207
up_read(&uts_sem);
1208+
if (copy_to_user(name, &tmp, sizeof(tmp)))
1209+
return -EFAULT;
12081210

1209-
if (!error && override_release(name->release, sizeof(name->release)))
1210-
error = -EFAULT;
1211-
if (!error && override_architecture(name))
1212-
error = -EFAULT;
1213-
return error;
1211+
if (override_release(name->release, sizeof(name->release)))
1212+
return -EFAULT;
1213+
if (override_architecture(name))
1214+
return -EFAULT;
1215+
return 0;
12141216
}
12151217

12161218
SYSCALL_DEFINE1(olduname, struct oldold_utsname __user *, name)
12171219
{
1218-
int error;
1220+
struct oldold_utsname tmp = {};
12191221

12201222
if (!name)
12211223
return -EFAULT;
1222-
if (!access_ok(VERIFY_WRITE, name, sizeof(struct oldold_utsname)))
1223-
return -EFAULT;
12241224

12251225
down_read(&uts_sem);
1226-
error = __copy_to_user(&name->sysname, &utsname()->sysname,
1227-
__OLD_UTS_LEN);
1228-
error |= __put_user(0, name->sysname + __OLD_UTS_LEN);
1229-
error |= __copy_to_user(&name->nodename, &utsname()->nodename,
1230-
__OLD_UTS_LEN);
1231-
error |= __put_user(0, name->nodename + __OLD_UTS_LEN);
1232-
error |= __copy_to_user(&name->release, &utsname()->release,
1233-
__OLD_UTS_LEN);
1234-
error |= __put_user(0, name->release + __OLD_UTS_LEN);
1235-
error |= __copy_to_user(&name->version, &utsname()->version,
1236-
__OLD_UTS_LEN);
1237-
error |= __put_user(0, name->version + __OLD_UTS_LEN);
1238-
error |= __copy_to_user(&name->machine, &utsname()->machine,
1239-
__OLD_UTS_LEN);
1240-
error |= __put_user(0, name->machine + __OLD_UTS_LEN);
1226+
memcpy(&tmp.sysname, &utsname()->sysname, __OLD_UTS_LEN);
1227+
memcpy(&tmp.nodename, &utsname()->nodename, __OLD_UTS_LEN);
1228+
memcpy(&tmp.release, &utsname()->release, __OLD_UTS_LEN);
1229+
memcpy(&tmp.version, &utsname()->version, __OLD_UTS_LEN);
1230+
memcpy(&tmp.machine, &utsname()->machine, __OLD_UTS_LEN);
12411231
up_read(&uts_sem);
1232+
if (copy_to_user(name, &tmp, sizeof(tmp)))
1233+
return -EFAULT;
12421234

1243-
if (!error && override_architecture(name))
1244-
error = -EFAULT;
1245-
if (!error && override_release(name->release, sizeof(name->release)))
1246-
error = -EFAULT;
1247-
return error ? -EFAULT : 0;
1235+
if (override_architecture(name))
1236+
return -EFAULT;
1237+
if (override_release(name->release, sizeof(name->release)))
1238+
return -EFAULT;
1239+
return 0;
12481240
}
12491241
#endif
12501242

@@ -1258,26 +1250,28 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)
12581250

12591251
if (len < 0 || len > __NEW_UTS_LEN)
12601252
return -EINVAL;
1261-
down_write(&uts_sem);
12621253
errno = -EFAULT;
12631254
if (!copy_from_user(tmp, name, len)) {
1264-
struct new_utsname *u = utsname();
1255+
struct new_utsname *u;
12651256

1257+
down_write(&uts_sem);
1258+
u = utsname();
12661259
memcpy(u->nodename, tmp, len);
12671260
memset(u->nodename + len, 0, sizeof(u->nodename) - len);
12681261
errno = 0;
12691262
uts_proc_notify(UTS_PROC_HOSTNAME);
1263+
up_write(&uts_sem);
12701264
}
1271-
up_write(&uts_sem);
12721265
return errno;
12731266
}
12741267

12751268
#ifdef __ARCH_WANT_SYS_GETHOSTNAME
12761269

12771270
SYSCALL_DEFINE2(gethostname, char __user *, name, int, len)
12781271
{
1279-
int i, errno;
1272+
int i;
12801273
struct new_utsname *u;
1274+
char tmp[__NEW_UTS_LEN + 1];
12811275

12821276
if (len < 0)
12831277
return -EINVAL;
@@ -1286,11 +1280,11 @@ SYSCALL_DEFINE2(gethostname, char __user *, name, int, len)
12861280
i = 1 + strlen(u->nodename);
12871281
if (i > len)
12881282
i = len;
1289-
errno = 0;
1290-
if (copy_to_user(name, u->nodename, i))
1291-
errno = -EFAULT;
1283+
memcpy(tmp, u->nodename, i);
12921284
up_read(&uts_sem);
1293-
return errno;
1285+
if (copy_to_user(name, tmp, i))
1286+
return -EFAULT;
1287+
return 0;
12941288
}
12951289

12961290
#endif
@@ -1309,17 +1303,18 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len)
13091303
if (len < 0 || len > __NEW_UTS_LEN)
13101304
return -EINVAL;
13111305

1312-
down_write(&uts_sem);
13131306
errno = -EFAULT;
13141307
if (!copy_from_user(tmp, name, len)) {
1315-
struct new_utsname *u = utsname();
1308+
struct new_utsname *u;
13161309

1310+
down_write(&uts_sem);
1311+
u = utsname();
13171312
memcpy(u->domainname, tmp, len);
13181313
memset(u->domainname + len, 0, sizeof(u->domainname) - len);
13191314
errno = 0;
13201315
uts_proc_notify(UTS_PROC_DOMAINNAME);
1316+
up_write(&uts_sem);
13211317
}
1322-
up_write(&uts_sem);
13231318
return errno;
13241319
}
13251320

0 commit comments

Comments
 (0)