mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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: 1da177e4c3 ("Linux-2.6.12-rc2")
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
			
			
This commit is contained in:
		
							parent
							
								
									5820f140ed
								
							
						
					
					
						commit
						42a0cc3478
					
				
					 5 changed files with 121 additions and 112 deletions
				
			
		| 
						 | 
					@ -530,24 +530,19 @@ SYSCALL_DEFINE4(osf_mount, unsigned long, typenr, const char __user *, path,
 | 
				
			||||||
SYSCALL_DEFINE1(osf_utsname, char __user *, name)
 | 
					SYSCALL_DEFINE1(osf_utsname, char __user *, name)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int error;
 | 
						int error;
 | 
				
			||||||
 | 
						char tmp[5 * 32];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	down_read(&uts_sem);
 | 
						down_read(&uts_sem);
 | 
				
			||||||
	error = -EFAULT;
 | 
						memcpy(tmp + 0 * 32, utsname()->sysname, 32);
 | 
				
			||||||
	if (copy_to_user(name + 0, utsname()->sysname, 32))
 | 
						memcpy(tmp + 1 * 32, utsname()->nodename, 32);
 | 
				
			||||||
		goto out;
 | 
						memcpy(tmp + 2 * 32, utsname()->release, 32);
 | 
				
			||||||
	if (copy_to_user(name + 32, utsname()->nodename, 32))
 | 
						memcpy(tmp + 3 * 32, utsname()->version, 32);
 | 
				
			||||||
		goto out;
 | 
						memcpy(tmp + 4 * 32, utsname()->machine, 32);
 | 
				
			||||||
	if (copy_to_user(name + 64, utsname()->release, 32))
 | 
						up_read(&uts_sem);
 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	if (copy_to_user(name + 96, utsname()->version, 32))
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	if (copy_to_user(name + 128, utsname()->machine, 32))
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	error = 0;
 | 
						if (copy_to_user(name, tmp, sizeof(tmp)))
 | 
				
			||||||
 out:
 | 
							return -EFAULT;
 | 
				
			||||||
	up_read(&uts_sem);	
 | 
						return 0;
 | 
				
			||||||
	return error;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SYSCALL_DEFINE0(getpagesize)
 | 
					SYSCALL_DEFINE0(getpagesize)
 | 
				
			||||||
| 
						 | 
					@ -567,18 +562,21 @@ SYSCALL_DEFINE2(osf_getdomainname, char __user *, name, int, namelen)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int len, err = 0;
 | 
						int len, err = 0;
 | 
				
			||||||
	char *kname;
 | 
						char *kname;
 | 
				
			||||||
 | 
						char tmp[32];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (namelen > 32)
 | 
						if (namelen < 0 || namelen > 32)
 | 
				
			||||||
		namelen = 32;
 | 
							namelen = 32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	down_read(&uts_sem);
 | 
						down_read(&uts_sem);
 | 
				
			||||||
	kname = utsname()->domainname;
 | 
						kname = utsname()->domainname;
 | 
				
			||||||
	len = strnlen(kname, namelen);
 | 
						len = strnlen(kname, namelen);
 | 
				
			||||||
	if (copy_to_user(name, kname, min(len + 1, namelen)))
 | 
						len = min(len + 1, namelen);
 | 
				
			||||||
		err = -EFAULT;
 | 
						memcpy(tmp, kname, len);
 | 
				
			||||||
	up_read(&uts_sem);
 | 
						up_read(&uts_sem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return err;
 | 
						if (copy_to_user(name, tmp, len))
 | 
				
			||||||
 | 
							return -EFAULT;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -739,13 +737,14 @@ SYSCALL_DEFINE3(osf_sysinfo, int, command, char __user *, buf, long, count)
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	unsigned long offset;
 | 
						unsigned long offset;
 | 
				
			||||||
	const char *res;
 | 
						const char *res;
 | 
				
			||||||
	long len, err = -EINVAL;
 | 
						long len;
 | 
				
			||||||
 | 
						char tmp[__NEW_UTS_LEN + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	offset = command-1;
 | 
						offset = command-1;
 | 
				
			||||||
	if (offset >= ARRAY_SIZE(sysinfo_table)) {
 | 
						if (offset >= ARRAY_SIZE(sysinfo_table)) {
 | 
				
			||||||
		/* Digital UNIX has a few unpublished interfaces here */
 | 
							/* Digital UNIX has a few unpublished interfaces here */
 | 
				
			||||||
		printk("sysinfo(%d)", command);
 | 
							printk("sysinfo(%d)", command);
 | 
				
			||||||
		goto out;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	down_read(&uts_sem);
 | 
						down_read(&uts_sem);
 | 
				
			||||||
| 
						 | 
					@ -753,13 +752,11 @@ SYSCALL_DEFINE3(osf_sysinfo, int, command, char __user *, buf, long, count)
 | 
				
			||||||
	len = strlen(res)+1;
 | 
						len = strlen(res)+1;
 | 
				
			||||||
	if ((unsigned long)len > (unsigned long)count)
 | 
						if ((unsigned long)len > (unsigned long)count)
 | 
				
			||||||
		len = count;
 | 
							len = count;
 | 
				
			||||||
	if (copy_to_user(buf, res, len))
 | 
						memcpy(tmp, res, len);
 | 
				
			||||||
		err = -EFAULT;
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		err = 0;
 | 
					 | 
				
			||||||
	up_read(&uts_sem);
 | 
						up_read(&uts_sem);
 | 
				
			||||||
 out:
 | 
						if (copy_to_user(buf, tmp, len))
 | 
				
			||||||
	return err;
 | 
							return -EFAULT;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SYSCALL_DEFINE5(osf_getsysinfo, unsigned long, op, void __user *, buffer,
 | 
					SYSCALL_DEFINE5(osf_getsysinfo, unsigned long, op, void __user *, buffer,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -197,23 +197,27 @@ SYSCALL_DEFINE5(rt_sigaction, int, sig,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SYSCALL_DEFINE2(getdomainname, char __user *, name, int, len)
 | 
					SYSCALL_DEFINE2(getdomainname, char __user *, name, int, len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 	int nlen, err;
 | 
						int nlen, err;
 | 
				
			||||||
 	
 | 
						char tmp[__NEW_UTS_LEN + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (len < 0)
 | 
						if (len < 0)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	down_read(&uts_sem);
 | 
						down_read(&uts_sem);
 | 
				
			||||||
 	
 | 
					
 | 
				
			||||||
	nlen = strlen(utsname()->domainname) + 1;
 | 
						nlen = strlen(utsname()->domainname) + 1;
 | 
				
			||||||
	err = -EINVAL;
 | 
						err = -EINVAL;
 | 
				
			||||||
	if (nlen > len)
 | 
						if (nlen > len)
 | 
				
			||||||
		goto out;
 | 
							goto out_unlock;
 | 
				
			||||||
 | 
						memcpy(tmp, utsname()->domainname, nlen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = -EFAULT;
 | 
						up_read(&uts_sem);
 | 
				
			||||||
	if (!copy_to_user(name, utsname()->domainname, nlen))
 | 
					 | 
				
			||||||
		err = 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
out:
 | 
						if (copy_to_user(name, tmp, nlen))
 | 
				
			||||||
 | 
							return -EFAULT;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out_unlock:
 | 
				
			||||||
	up_read(&uts_sem);
 | 
						up_read(&uts_sem);
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -519,23 +519,27 @@ asmlinkage void sparc_breakpoint(struct pt_regs *regs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SYSCALL_DEFINE2(getdomainname, char __user *, name, int, len)
 | 
					SYSCALL_DEFINE2(getdomainname, char __user *, name, int, len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
        int nlen, err;
 | 
						int nlen, err;
 | 
				
			||||||
 | 
						char tmp[__NEW_UTS_LEN + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (len < 0)
 | 
						if (len < 0)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	down_read(&uts_sem);
 | 
						down_read(&uts_sem);
 | 
				
			||||||
 	
 | 
					
 | 
				
			||||||
	nlen = strlen(utsname()->domainname) + 1;
 | 
						nlen = strlen(utsname()->domainname) + 1;
 | 
				
			||||||
	err = -EINVAL;
 | 
						err = -EINVAL;
 | 
				
			||||||
	if (nlen > len)
 | 
						if (nlen > len)
 | 
				
			||||||
		goto out;
 | 
							goto out_unlock;
 | 
				
			||||||
 | 
						memcpy(tmp, utsname()->domainname, nlen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = -EFAULT;
 | 
						up_read(&uts_sem);
 | 
				
			||||||
	if (!copy_to_user(name, utsname()->domainname, nlen))
 | 
					 | 
				
			||||||
		err = 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
out:
 | 
						if (copy_to_user(name, tmp, nlen))
 | 
				
			||||||
 | 
							return -EFAULT;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out_unlock:
 | 
				
			||||||
	up_read(&uts_sem);
 | 
						up_read(&uts_sem);
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										95
									
								
								kernel/sys.c
									
									
									
									
									
								
							
							
						
						
									
										95
									
								
								kernel/sys.c
									
									
									
									
									
								
							| 
						 | 
					@ -1237,18 +1237,19 @@ static int override_release(char __user *release, size_t len)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
 | 
					SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int errno = 0;
 | 
						struct new_utsname tmp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	down_read(&uts_sem);
 | 
						down_read(&uts_sem);
 | 
				
			||||||
	if (copy_to_user(name, utsname(), sizeof *name))
 | 
						memcpy(&tmp, utsname(), sizeof(tmp));
 | 
				
			||||||
		errno = -EFAULT;
 | 
					 | 
				
			||||||
	up_read(&uts_sem);
 | 
						up_read(&uts_sem);
 | 
				
			||||||
 | 
						if (copy_to_user(name, &tmp, sizeof(tmp)))
 | 
				
			||||||
 | 
							return -EFAULT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!errno && override_release(name->release, sizeof(name->release)))
 | 
						if (override_release(name->release, sizeof(name->release)))
 | 
				
			||||||
		errno = -EFAULT;
 | 
							return -EFAULT;
 | 
				
			||||||
	if (!errno && override_architecture(name))
 | 
						if (override_architecture(name))
 | 
				
			||||||
		errno = -EFAULT;
 | 
							return -EFAULT;
 | 
				
			||||||
	return errno;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __ARCH_WANT_SYS_OLD_UNAME
 | 
					#ifdef __ARCH_WANT_SYS_OLD_UNAME
 | 
				
			||||||
| 
						 | 
					@ -1257,55 +1258,46 @@ SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
SYSCALL_DEFINE1(uname, struct old_utsname __user *, name)
 | 
					SYSCALL_DEFINE1(uname, struct old_utsname __user *, name)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int error = 0;
 | 
						struct old_utsname tmp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!name)
 | 
						if (!name)
 | 
				
			||||||
		return -EFAULT;
 | 
							return -EFAULT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	down_read(&uts_sem);
 | 
						down_read(&uts_sem);
 | 
				
			||||||
	if (copy_to_user(name, utsname(), sizeof(*name)))
 | 
						memcpy(&tmp, utsname(), sizeof(tmp));
 | 
				
			||||||
		error = -EFAULT;
 | 
					 | 
				
			||||||
	up_read(&uts_sem);
 | 
						up_read(&uts_sem);
 | 
				
			||||||
 | 
						if (copy_to_user(name, &tmp, sizeof(tmp)))
 | 
				
			||||||
 | 
							return -EFAULT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!error && override_release(name->release, sizeof(name->release)))
 | 
						if (override_release(name->release, sizeof(name->release)))
 | 
				
			||||||
		error = -EFAULT;
 | 
							return -EFAULT;
 | 
				
			||||||
	if (!error && override_architecture(name))
 | 
						if (override_architecture(name))
 | 
				
			||||||
		error = -EFAULT;
 | 
							return -EFAULT;
 | 
				
			||||||
	return error;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SYSCALL_DEFINE1(olduname, struct oldold_utsname __user *, name)
 | 
					SYSCALL_DEFINE1(olduname, struct oldold_utsname __user *, name)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int error;
 | 
						struct oldold_utsname tmp = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!name)
 | 
						if (!name)
 | 
				
			||||||
		return -EFAULT;
 | 
							return -EFAULT;
 | 
				
			||||||
	if (!access_ok(VERIFY_WRITE, name, sizeof(struct oldold_utsname)))
 | 
					 | 
				
			||||||
		return -EFAULT;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	down_read(&uts_sem);
 | 
						down_read(&uts_sem);
 | 
				
			||||||
	error = __copy_to_user(&name->sysname, &utsname()->sysname,
 | 
						memcpy(&tmp.sysname, &utsname()->sysname, __OLD_UTS_LEN);
 | 
				
			||||||
			       __OLD_UTS_LEN);
 | 
						memcpy(&tmp.nodename, &utsname()->nodename, __OLD_UTS_LEN);
 | 
				
			||||||
	error |= __put_user(0, name->sysname + __OLD_UTS_LEN);
 | 
						memcpy(&tmp.release, &utsname()->release, __OLD_UTS_LEN);
 | 
				
			||||||
	error |= __copy_to_user(&name->nodename, &utsname()->nodename,
 | 
						memcpy(&tmp.version, &utsname()->version, __OLD_UTS_LEN);
 | 
				
			||||||
				__OLD_UTS_LEN);
 | 
						memcpy(&tmp.machine, &utsname()->machine, __OLD_UTS_LEN);
 | 
				
			||||||
	error |= __put_user(0, name->nodename + __OLD_UTS_LEN);
 | 
					 | 
				
			||||||
	error |= __copy_to_user(&name->release, &utsname()->release,
 | 
					 | 
				
			||||||
				__OLD_UTS_LEN);
 | 
					 | 
				
			||||||
	error |= __put_user(0, name->release + __OLD_UTS_LEN);
 | 
					 | 
				
			||||||
	error |= __copy_to_user(&name->version, &utsname()->version,
 | 
					 | 
				
			||||||
				__OLD_UTS_LEN);
 | 
					 | 
				
			||||||
	error |= __put_user(0, name->version + __OLD_UTS_LEN);
 | 
					 | 
				
			||||||
	error |= __copy_to_user(&name->machine, &utsname()->machine,
 | 
					 | 
				
			||||||
				__OLD_UTS_LEN);
 | 
					 | 
				
			||||||
	error |= __put_user(0, name->machine + __OLD_UTS_LEN);
 | 
					 | 
				
			||||||
	up_read(&uts_sem);
 | 
						up_read(&uts_sem);
 | 
				
			||||||
 | 
						if (copy_to_user(name, &tmp, sizeof(tmp)))
 | 
				
			||||||
 | 
							return -EFAULT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!error && override_architecture(name))
 | 
						if (override_architecture(name))
 | 
				
			||||||
		error = -EFAULT;
 | 
							return -EFAULT;
 | 
				
			||||||
	if (!error && override_release(name->release, sizeof(name->release)))
 | 
						if (override_release(name->release, sizeof(name->release)))
 | 
				
			||||||
		error = -EFAULT;
 | 
							return -EFAULT;
 | 
				
			||||||
	return error ? -EFAULT : 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1319,17 +1311,18 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (len < 0 || len > __NEW_UTS_LEN)
 | 
						if (len < 0 || len > __NEW_UTS_LEN)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	down_write(&uts_sem);
 | 
					 | 
				
			||||||
	errno = -EFAULT;
 | 
						errno = -EFAULT;
 | 
				
			||||||
	if (!copy_from_user(tmp, name, len)) {
 | 
						if (!copy_from_user(tmp, name, len)) {
 | 
				
			||||||
		struct new_utsname *u = utsname();
 | 
							struct new_utsname *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							down_write(&uts_sem);
 | 
				
			||||||
 | 
							u = utsname();
 | 
				
			||||||
		memcpy(u->nodename, tmp, len);
 | 
							memcpy(u->nodename, tmp, len);
 | 
				
			||||||
		memset(u->nodename + len, 0, sizeof(u->nodename) - len);
 | 
							memset(u->nodename + len, 0, sizeof(u->nodename) - len);
 | 
				
			||||||
		errno = 0;
 | 
							errno = 0;
 | 
				
			||||||
		uts_proc_notify(UTS_PROC_HOSTNAME);
 | 
							uts_proc_notify(UTS_PROC_HOSTNAME);
 | 
				
			||||||
 | 
							up_write(&uts_sem);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	up_write(&uts_sem);
 | 
					 | 
				
			||||||
	return errno;
 | 
						return errno;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1337,8 +1330,9 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SYSCALL_DEFINE2(gethostname, char __user *, name, int, len)
 | 
					SYSCALL_DEFINE2(gethostname, char __user *, name, int, len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int i, errno;
 | 
						int i;
 | 
				
			||||||
	struct new_utsname *u;
 | 
						struct new_utsname *u;
 | 
				
			||||||
 | 
						char tmp[__NEW_UTS_LEN + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (len < 0)
 | 
						if (len < 0)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
| 
						 | 
					@ -1347,11 +1341,11 @@ SYSCALL_DEFINE2(gethostname, char __user *, name, int, len)
 | 
				
			||||||
	i = 1 + strlen(u->nodename);
 | 
						i = 1 + strlen(u->nodename);
 | 
				
			||||||
	if (i > len)
 | 
						if (i > len)
 | 
				
			||||||
		i = len;
 | 
							i = len;
 | 
				
			||||||
	errno = 0;
 | 
						memcpy(tmp, u->nodename, i);
 | 
				
			||||||
	if (copy_to_user(name, u->nodename, i))
 | 
					 | 
				
			||||||
		errno = -EFAULT;
 | 
					 | 
				
			||||||
	up_read(&uts_sem);
 | 
						up_read(&uts_sem);
 | 
				
			||||||
	return errno;
 | 
						if (copy_to_user(name, tmp, i))
 | 
				
			||||||
 | 
							return -EFAULT;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -1370,17 +1364,18 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len)
 | 
				
			||||||
	if (len < 0 || len > __NEW_UTS_LEN)
 | 
						if (len < 0 || len > __NEW_UTS_LEN)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	down_write(&uts_sem);
 | 
					 | 
				
			||||||
	errno = -EFAULT;
 | 
						errno = -EFAULT;
 | 
				
			||||||
	if (!copy_from_user(tmp, name, len)) {
 | 
						if (!copy_from_user(tmp, name, len)) {
 | 
				
			||||||
		struct new_utsname *u = utsname();
 | 
							struct new_utsname *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							down_write(&uts_sem);
 | 
				
			||||||
 | 
							u = utsname();
 | 
				
			||||||
		memcpy(u->domainname, tmp, len);
 | 
							memcpy(u->domainname, tmp, len);
 | 
				
			||||||
		memset(u->domainname + len, 0, sizeof(u->domainname) - len);
 | 
							memset(u->domainname + len, 0, sizeof(u->domainname) - len);
 | 
				
			||||||
		errno = 0;
 | 
							errno = 0;
 | 
				
			||||||
		uts_proc_notify(UTS_PROC_DOMAINNAME);
 | 
							uts_proc_notify(UTS_PROC_DOMAINNAME);
 | 
				
			||||||
 | 
							up_write(&uts_sem);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	up_write(&uts_sem);
 | 
					 | 
				
			||||||
	return errno;
 | 
						return errno;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_PROC_SYSCTL
 | 
					#ifdef CONFIG_PROC_SYSCTL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void *get_uts(struct ctl_table *table, int write)
 | 
					static void *get_uts(struct ctl_table *table)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char *which = table->data;
 | 
						char *which = table->data;
 | 
				
			||||||
	struct uts_namespace *uts_ns;
 | 
						struct uts_namespace *uts_ns;
 | 
				
			||||||
| 
						 | 
					@ -26,21 +26,9 @@ static void *get_uts(struct ctl_table *table, int write)
 | 
				
			||||||
	uts_ns = current->nsproxy->uts_ns;
 | 
						uts_ns = current->nsproxy->uts_ns;
 | 
				
			||||||
	which = (which - (char *)&init_uts_ns) + (char *)uts_ns;
 | 
						which = (which - (char *)&init_uts_ns) + (char *)uts_ns;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!write)
 | 
					 | 
				
			||||||
		down_read(&uts_sem);
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		down_write(&uts_sem);
 | 
					 | 
				
			||||||
	return which;
 | 
						return which;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void put_uts(struct ctl_table *table, int write, void *which)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!write)
 | 
					 | 
				
			||||||
		up_read(&uts_sem);
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		up_write(&uts_sem);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 *	Special case of dostring for the UTS structure. This has locks
 | 
					 *	Special case of dostring for the UTS structure. This has locks
 | 
				
			||||||
 *	to observe. Should this be in kernel/sys.c ????
 | 
					 *	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,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct ctl_table uts_table;
 | 
						struct ctl_table uts_table;
 | 
				
			||||||
	int r;
 | 
						int r;
 | 
				
			||||||
	memcpy(&uts_table, table, sizeof(uts_table));
 | 
						char tmp_data[__NEW_UTS_LEN + 1];
 | 
				
			||||||
	uts_table.data = get_uts(table, write);
 | 
					 | 
				
			||||||
	r = proc_dostring(&uts_table, write, buffer, lenp, ppos);
 | 
					 | 
				
			||||||
	put_uts(table, write, uts_table.data);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (write)
 | 
						memcpy(&uts_table, table, sizeof(uts_table));
 | 
				
			||||||
 | 
						uts_table.data = tmp_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Buffer the value in tmp_data so that proc_dostring() can be called
 | 
				
			||||||
 | 
						 * without holding any locks.
 | 
				
			||||||
 | 
						 * We also need to read the original value in the write==1 case to
 | 
				
			||||||
 | 
						 * support partial writes.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						down_read(&uts_sem);
 | 
				
			||||||
 | 
						memcpy(tmp_data, get_uts(table), sizeof(tmp_data));
 | 
				
			||||||
 | 
						up_read(&uts_sem);
 | 
				
			||||||
 | 
						r = proc_dostring(&uts_table, write, buffer, lenp, ppos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (write) {
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Write back the new value.
 | 
				
			||||||
 | 
							 * Note that, since we dropped uts_sem, the result can
 | 
				
			||||||
 | 
							 * theoretically be incorrect if there are two parallel writes
 | 
				
			||||||
 | 
							 * at non-zero offsets to the same sysctl.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							down_write(&uts_sem);
 | 
				
			||||||
 | 
							memcpy(get_uts(table), tmp_data, sizeof(tmp_data));
 | 
				
			||||||
 | 
							up_write(&uts_sem);
 | 
				
			||||||
		proc_sys_poll_notify(table->poll);
 | 
							proc_sys_poll_notify(table->poll);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return r;
 | 
						return r;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue