Commit d7c9e99a authored by Alexey Gladkov's avatar Alexey Gladkov Committed by Eric W. Biederman
Browse files

Reimplement RLIMIT_MEMLOCK on top of ucounts



The rlimit counter is tied to uid in the user_namespace. This allows
rlimit values to be specified in userns even if they are already
globally exceeded by the user. However, the value of the previous
user_namespaces cannot be exceeded.

Changelog

v11:
* Fix issue found by lkp robot.

v8:
* Fix issues found by lkp-tests project.

v7:
* Keep only ucounts for RLIMIT_MEMLOCK checks instead of struct cred.

v6:
* Fix bug in hugetlb_file_setup() detected by trinity.

Reported-by: default avatarkernel test robot <oliver.sang@intel.com>
Reported-by: default avatarkernel test robot <lkp@intel.com>
Signed-off-by: default avatarAlexey Gladkov <legion@kernel.org>
Link: https://lkml.kernel.org/r/970d50c70c71bfd4496e0e8d2a0a32feebebb350.1619094428.git.legion@kernel.org


Signed-off-by: default avatarEric W. Biederman <ebiederm@xmission.com>
parent d6469690
Loading
Loading
Loading
Loading
+8 −8
Original line number Diff line number Diff line
@@ -1443,7 +1443,7 @@ static int get_hstate_idx(int page_size_log)
 * otherwise hugetlb_reserve_pages reserves one less hugepages than intended.
 */
struct file *hugetlb_file_setup(const char *name, size_t size,
				vm_flags_t acctflag, struct user_struct **user,
				vm_flags_t acctflag, struct ucounts **ucounts,
				int creat_flags, int page_size_log)
{
	struct inode *inode;
@@ -1455,20 +1455,20 @@ struct file *hugetlb_file_setup(const char *name, size_t size,
	if (hstate_idx < 0)
		return ERR_PTR(-ENODEV);

	*user = NULL;
	*ucounts = NULL;
	mnt = hugetlbfs_vfsmount[hstate_idx];
	if (!mnt)
		return ERR_PTR(-ENOENT);

	if (creat_flags == HUGETLB_SHMFS_INODE && !can_do_hugetlb_shm()) {
		*user = current_user();
		if (user_shm_lock(size, *user)) {
		*ucounts = current_ucounts();
		if (user_shm_lock(size, *ucounts)) {
			task_lock(current);
			pr_warn_once("%s (%d): Using mlock ulimits for SHM_HUGETLB is deprecated\n",
				current->comm, current->pid);
			task_unlock(current);
		} else {
			*user = NULL;
			*ucounts = NULL;
			return ERR_PTR(-EPERM);
		}
	}
@@ -1495,9 +1495,9 @@ struct file *hugetlb_file_setup(const char *name, size_t size,

	iput(inode);
out:
	if (*user) {
		user_shm_unlock(size, *user);
		*user = NULL;
	if (*ucounts) {
		user_shm_unlock(size, *ucounts);
		*ucounts = NULL;
	}
	return file;
}
+2 −2
Original line number Diff line number Diff line
@@ -434,7 +434,7 @@ static inline struct hugetlbfs_inode_info *HUGETLBFS_I(struct inode *inode)
extern const struct file_operations hugetlbfs_file_operations;
extern const struct vm_operations_struct hugetlb_vm_ops;
struct file *hugetlb_file_setup(const char *name, size_t size, vm_flags_t acct,
				struct user_struct **user, int creat_flags,
				struct ucounts **ucounts, int creat_flags,
				int page_size_log);

static inline bool is_file_hugepages(struct file *file)
@@ -454,7 +454,7 @@ static inline struct hstate *hstate_inode(struct inode *i)
#define is_file_hugepages(file)			false
static inline struct file *
hugetlb_file_setup(const char *name, size_t size, vm_flags_t acctflag,
		struct user_struct **user, int creat_flags,
		struct ucounts **ucounts, int creat_flags,
		int page_size_log)
{
	return ERR_PTR(-ENOSYS);
+2 −2
Original line number Diff line number Diff line
@@ -1670,8 +1670,8 @@ extern bool can_do_mlock(void);
#else
static inline bool can_do_mlock(void) { return false; }
#endif
extern int user_shm_lock(size_t, struct user_struct *);
extern void user_shm_unlock(size_t, struct user_struct *);
extern int user_shm_lock(size_t, struct ucounts *);
extern void user_shm_unlock(size_t, struct ucounts *);

/*
 * Parameter block passed down to zap_pte_range in exceptional cases.
+0 −1
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ struct user_struct {
#ifdef CONFIG_EPOLL
	atomic_long_t epoll_watches; /* The number of file descriptors currently watched */
#endif
	unsigned long locked_shm; /* How many pages of mlocked shm ? */
	unsigned long unix_inflight;	/* How many files in flight in unix sockets */
	atomic_long_t pipe_bufs;  /* how many pages are allocated in pipe buffers */

+1 −1
Original line number Diff line number Diff line
@@ -65,7 +65,7 @@ extern struct file *shmem_file_setup_with_mnt(struct vfsmount *mnt,
extern int shmem_zero_setup(struct vm_area_struct *);
extern unsigned long shmem_get_unmapped_area(struct file *, unsigned long addr,
		unsigned long len, unsigned long pgoff, unsigned long flags);
extern int shmem_lock(struct file *file, int lock, struct user_struct *user);
extern int shmem_lock(struct file *file, int lock, struct ucounts *ucounts);
#ifdef CONFIG_SHMEM
extern const struct address_space_operations shmem_aops;
static inline bool shmem_mapping(struct address_space *mapping)
Loading