forked from mirrors/linux
		
	ipc,shm: shorten critical region for shmctl
With the *_INFO, *_STAT, IPC_RMID and IPC_SET commands already optimized, deal with the remaining SHM_LOCK and SHM_UNLOCK commands. Take the shm_perm lock after doing the initial auditing and security checks. The rest of the logic remains unchanged. Signed-off-by: Davidlohr Bueso <davidlohr.bueso@hp.com> Tested-by: Sedat Dilek <sedat.dilek@gmail.com> Cc: Rik van Riel <riel@redhat.com> Cc: Manfred Spraul <manfred@colorfullife.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									c97cb9ccab
								
							
						
					
					
						commit
						2caacaa82a
					
				
					 1 changed files with 25 additions and 24 deletions
				
			
		
							
								
								
									
										49
									
								
								ipc/shm.c
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								ipc/shm.c
									
									
									
									
									
								
							| 
						 | 
					@ -940,10 +940,8 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
 | 
				
			||||||
	int err, version;
 | 
						int err, version;
 | 
				
			||||||
	struct ipc_namespace *ns;
 | 
						struct ipc_namespace *ns;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (cmd < 0 || shmid < 0) {
 | 
						if (cmd < 0 || shmid < 0)
 | 
				
			||||||
		err = -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	version = ipc_parse_version(&cmd);
 | 
						version = ipc_parse_version(&cmd);
 | 
				
			||||||
	ns = current->nsproxy->ipc_ns;
 | 
						ns = current->nsproxy->ipc_ns;
 | 
				
			||||||
| 
						 | 
					@ -954,36 +952,40 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
 | 
				
			||||||
	case SHM_STAT:
 | 
						case SHM_STAT:
 | 
				
			||||||
	case IPC_STAT:
 | 
						case IPC_STAT:
 | 
				
			||||||
		return shmctl_nolock(ns, shmid, cmd, version, buf);
 | 
							return shmctl_nolock(ns, shmid, cmd, version, buf);
 | 
				
			||||||
 | 
						case IPC_RMID:
 | 
				
			||||||
 | 
						case IPC_SET:
 | 
				
			||||||
 | 
							return shmctl_down(ns, shmid, cmd, buf, version);
 | 
				
			||||||
	case SHM_LOCK:
 | 
						case SHM_LOCK:
 | 
				
			||||||
	case SHM_UNLOCK:
 | 
						case SHM_UNLOCK:
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		struct file *shm_file;
 | 
							struct file *shm_file;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		shp = shm_lock_check(ns, shmid);
 | 
							rcu_read_lock();
 | 
				
			||||||
 | 
							shp = shm_obtain_object_check(ns, shmid);
 | 
				
			||||||
		if (IS_ERR(shp)) {
 | 
							if (IS_ERR(shp)) {
 | 
				
			||||||
			err = PTR_ERR(shp);
 | 
								err = PTR_ERR(shp);
 | 
				
			||||||
			goto out;
 | 
								goto out_unlock1;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		audit_ipc_obj(&(shp->shm_perm));
 | 
							audit_ipc_obj(&(shp->shm_perm));
 | 
				
			||||||
 | 
							err = security_shm_shmctl(shp, cmd);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								goto out_unlock1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ipc_lock_object(&shp->shm_perm);
 | 
				
			||||||
		if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) {
 | 
							if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) {
 | 
				
			||||||
			kuid_t euid = current_euid();
 | 
								kuid_t euid = current_euid();
 | 
				
			||||||
			err = -EPERM;
 | 
								err = -EPERM;
 | 
				
			||||||
			if (!uid_eq(euid, shp->shm_perm.uid) &&
 | 
								if (!uid_eq(euid, shp->shm_perm.uid) &&
 | 
				
			||||||
			    !uid_eq(euid, shp->shm_perm.cuid))
 | 
								    !uid_eq(euid, shp->shm_perm.cuid))
 | 
				
			||||||
				goto out_unlock;
 | 
									goto out_unlock0;
 | 
				
			||||||
			if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK))
 | 
								if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK))
 | 
				
			||||||
				goto out_unlock;
 | 
									goto out_unlock0;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		err = security_shm_shmctl(shp, cmd);
 | 
					 | 
				
			||||||
		if (err)
 | 
					 | 
				
			||||||
			goto out_unlock;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		shm_file = shp->shm_file;
 | 
							shm_file = shp->shm_file;
 | 
				
			||||||
		if (is_file_hugepages(shm_file))
 | 
							if (is_file_hugepages(shm_file))
 | 
				
			||||||
			goto out_unlock;
 | 
								goto out_unlock0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (cmd == SHM_LOCK) {
 | 
							if (cmd == SHM_LOCK) {
 | 
				
			||||||
			struct user_struct *user = current_user();
 | 
								struct user_struct *user = current_user();
 | 
				
			||||||
| 
						 | 
					@ -992,32 +994,31 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
 | 
				
			||||||
				shp->shm_perm.mode |= SHM_LOCKED;
 | 
									shp->shm_perm.mode |= SHM_LOCKED;
 | 
				
			||||||
				shp->mlock_user = user;
 | 
									shp->mlock_user = user;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			goto out_unlock;
 | 
								goto out_unlock0;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* SHM_UNLOCK */
 | 
							/* SHM_UNLOCK */
 | 
				
			||||||
		if (!(shp->shm_perm.mode & SHM_LOCKED))
 | 
							if (!(shp->shm_perm.mode & SHM_LOCKED))
 | 
				
			||||||
			goto out_unlock;
 | 
								goto out_unlock0;
 | 
				
			||||||
		shmem_lock(shm_file, 0, shp->mlock_user);
 | 
							shmem_lock(shm_file, 0, shp->mlock_user);
 | 
				
			||||||
		shp->shm_perm.mode &= ~SHM_LOCKED;
 | 
							shp->shm_perm.mode &= ~SHM_LOCKED;
 | 
				
			||||||
		shp->mlock_user = NULL;
 | 
							shp->mlock_user = NULL;
 | 
				
			||||||
		get_file(shm_file);
 | 
							get_file(shm_file);
 | 
				
			||||||
		shm_unlock(shp);
 | 
							ipc_unlock_object(&shp->shm_perm);
 | 
				
			||||||
 | 
							rcu_read_unlock();
 | 
				
			||||||
		shmem_unlock_mapping(shm_file->f_mapping);
 | 
							shmem_unlock_mapping(shm_file->f_mapping);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		fput(shm_file);
 | 
							fput(shm_file);
 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	case IPC_RMID:
 | 
					 | 
				
			||||||
	case IPC_SET:
 | 
					 | 
				
			||||||
		err = shmctl_down(ns, shmid, cmd, buf, version);
 | 
					 | 
				
			||||||
		return err;
 | 
							return err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out_unlock:
 | 
					out_unlock0:
 | 
				
			||||||
	shm_unlock(shp);
 | 
						ipc_unlock_object(&shp->shm_perm);
 | 
				
			||||||
out:
 | 
					out_unlock1:
 | 
				
			||||||
 | 
						rcu_read_unlock();
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue