summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/Makefile1
-rw-r--r--kernel/bpf/bpf_iter.c6
-rw-r--r--kernel/bpf/inode.c6
-rw-r--r--kernel/bpf/token.c6
-rw-r--r--kernel/capability.c4
-rw-r--r--kernel/exit.c15
-rw-r--r--kernel/fork.c68
-rw-r--r--kernel/kthread.c2
-rw-r--r--kernel/pid_namespace.c3
-rw-r--r--kernel/ptrace.c6
-rw-r--r--kernel/signal.c14
-rw-r--r--kernel/tests/.kunitconfig4
-rw-r--r--kernel/tests/user_ns_map_kunit.c98
-rw-r--r--kernel/user_namespace.c60
-rw-r--r--kernel/utsname.c1
15 files changed, 192 insertions, 102 deletions
diff --git a/kernel/Makefile b/kernel/Makefile
index 1e1a31673577..2a64282749b8 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -141,6 +141,7 @@ obj-$(CONFIG_WATCH_QUEUE) += watch_queue.o
obj-$(CONFIG_RESOURCE_KUNIT_TEST) += resource_kunit.o
obj-$(CONFIG_SYSCTL_KUNIT_TEST) += sysctl-test.o
+obj-$(CONFIG_USER_NS_MAP_KUNIT_TEST) += tests/user_ns_map_kunit.o
CFLAGS_kstack_erase.o += $(DISABLE_KSTACK_ERASE)
CFLAGS_kstack_erase.o += $(call cc-option,-mgeneral-regs-only)
diff --git a/kernel/bpf/bpf_iter.c b/kernel/bpf/bpf_iter.c
index b40eb404adab..d9191df5de22 100644
--- a/kernel/bpf/bpf_iter.c
+++ b/kernel/bpf/bpf_iter.c
@@ -643,11 +643,11 @@ int bpf_iter_new_fd(struct bpf_link *link)
flags = O_RDONLY | O_CLOEXEC;
FD_PREPARE(fdf, flags, anon_inode_getfile("bpf_iter", &bpf_iter_fops, NULL, flags));
- if (fdf.err)
- return fdf.err;
+ if (fdf->fd < 0)
+ return fdf->fd;
iter_link = container_of(link, struct bpf_iter_link, link);
- err = prepare_seq_file(fd_prepare_file(fdf), iter_link);
+ err = prepare_seq_file(fdf->file, iter_link);
if (err)
return err; /* Automatic cleanup handles fput */
diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
index 7837968c0842..c6f328e4752e 100644
--- a/kernel/bpf/inode.c
+++ b/kernel/bpf/inode.c
@@ -176,7 +176,7 @@ static void bpf_dentry_finalize(struct dentry *dentry, struct inode *inode,
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
}
-static struct dentry *bpf_mkdir(struct mnt_idmap *idmap, struct inode *dir,
+static struct dentry *bpf_mkdir(const struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode)
{
struct inode *inode;
@@ -424,7 +424,7 @@ bpf_lookup(struct inode *dir, struct dentry *dentry, unsigned flags)
return simple_lookup(dir, dentry, flags);
}
-static int bpf_symlink(struct mnt_idmap *idmap, struct inode *dir,
+static int bpf_symlink(const struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, const char *target)
{
struct inode *inode;
@@ -874,7 +874,7 @@ enum {
};
static int bpf_fs_xattr_set(const struct xattr_handler *handler,
- struct mnt_idmap *idmap, struct dentry *unused,
+ const struct mnt_idmap *idmap, struct dentry *unused,
struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
diff --git a/kernel/bpf/token.c b/kernel/bpf/token.c
index e85a179523f0..da915a4f972b 100644
--- a/kernel/bpf/token.c
+++ b/kernel/bpf/token.c
@@ -169,8 +169,8 @@ int bpf_token_create(union bpf_attr *attr)
FD_PREPARE(fdf, O_CLOEXEC,
alloc_file_pseudo(inode, path.mnt, BPF_TOKEN_INODE_NAME,
O_RDWR, &bpf_token_fops));
- if (fdf.err)
- return fdf.err;
+ if (fdf->fd < 0)
+ return fdf->fd;
token = kzalloc_obj(*token, GFP_USER);
if (!token)
@@ -190,7 +190,7 @@ int bpf_token_create(union bpf_attr *attr)
return err;
get_user_ns(token->userns);
- fd_prepare_file(fdf)->private_data = no_free_ptr(token);
+ fdf->file->private_data = no_free_ptr(token);
return fd_publish(fdf);
}
diff --git a/kernel/capability.c b/kernel/capability.c
index 90e6ab62f6db..a689dae590ff 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -470,7 +470,7 @@ EXPORT_SYMBOL(file_ns_capable);
* Return true if the inode uid and gid are within the namespace.
*/
bool privileged_wrt_inode_uidgid(struct user_namespace *ns,
- struct mnt_idmap *idmap,
+ const struct mnt_idmap *idmap,
const struct inode *inode)
{
return vfsuid_has_mapping(ns, i_uid_into_vfsuid(idmap, inode)) &&
@@ -487,7 +487,7 @@ bool privileged_wrt_inode_uidgid(struct user_namespace *ns,
* its own user namespace and that the given inode's uid and gid are
* mapped into the current user namespace.
*/
-bool capable_wrt_inode_uidgid(struct mnt_idmap *idmap,
+bool capable_wrt_inode_uidgid(const struct mnt_idmap *idmap,
const struct inode *inode, int cap)
{
struct user_namespace *ns = current_user_ns();
diff --git a/kernel/exit.c b/kernel/exit.c
index 282328d2b4cf..29e853a36602 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -17,6 +17,7 @@
#include <linux/module.h>
#include <linux/capability.h>
#include <linux/completion.h>
+#include <linux/wait_bit.h>
#include <linux/personality.h>
#include <linux/tty.h>
#include <linux/iocontext.h>
@@ -436,15 +437,14 @@ static void coredump_task_exit(struct task_struct *tsk,
self.task = tsk;
if (self.task->flags & PF_SIGNALED)
- self.next = xchg(&core_state->dumper.next, &self);
+ self.next = xchg(&core_state->tasks, &self);
else
self.task = NULL;
/*
* Implies mb(), the result of xchg() must be visible
- * to core_state->dumper.
+ * to the dumper.
*/
- if (atomic_dec_and_test(&core_state->nr_threads))
- complete(&core_state->startup);
+ atomic_dec_and_wake_up(&core_state->threads_remaining);
for (;;) {
set_current_state(TASK_IDLE|TASK_FREEZABLE);
@@ -892,7 +892,7 @@ static void synchronize_group_exit(struct task_struct *tsk, long code)
* Serialize with any possible pending coredump.
* We must hold siglock around checking core_state
* and setting PF_POSTCOREDUMP. The core-inducing thread
- * will increment ->nr_threads for each thread in the
+ * will increment ->threads_remaining for each thread in the
* group without PF_POSTCOREDUMP set.
*/
tsk->flags |= PF_POSTCOREDUMP;
@@ -978,10 +978,11 @@ void __noreturn do_exit(long code)
exit_sem(tsk);
exit_shm(tsk);
- exit_files(tsk);
- exit_fs(tsk);
+ /* Hang the tty up before the last close of it can clear the session. */
if (group_dead)
disassociate_ctty(1);
+ exit_files(tsk);
+ exit_fs(tsk);
exit_nsproxy_namespaces(tsk);
exit_task_work(tsk);
exit_thread(tsk);
diff --git a/kernel/fork.c b/kernel/fork.c
index 10f2d05d816a..5a4cf4cf767a 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1677,6 +1677,7 @@ static int copy_files(u64 clone_flags, struct task_struct *tsk,
if (clone_flags & CLONE_FILES) {
atomic_inc(&oldf->count);
+ tsk->files = oldf;
return 0;
}
@@ -2146,12 +2147,9 @@ __latent_entropy struct task_struct *copy_process(
if (args->kthread)
p->flags |= PF_KTHREAD;
if (args->user_worker) {
- /*
- * Mark us a user worker, and block any signal that isn't
- * fatal or STOP
- */
+ /* A user worker takes only the signals nobody can block. */
p->flags |= PF_USER_WORKER;
- siginitsetinv(&p->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP));
+ siginitsetinv(&p->blocked, SIG_KERNEL_ONLY_MASK);
}
if (args->io_thread)
p->flags |= PF_IO_WORKER;
@@ -2200,6 +2198,8 @@ __latent_entropy struct task_struct *copy_process(
INIT_LIST_HEAD(&p->sibling);
rcu_copy_process(p);
p->vfork_done = NULL;
+ /* Set by copy_files(), exit_files() on the error path skips NULL. */
+ p->files = NULL;
spin_lock_init(&p->alloc_lock);
init_sigpending(&p->pending);
@@ -2301,7 +2301,7 @@ __latent_entropy struct task_struct *copy_process(
goto bad_fork_cleanup_semundo;
retval = copy_fs(clone_flags, p, args->umh);
if (retval)
- goto bad_fork_cleanup_files;
+ goto bad_fork_cleanup_semundo;
retval = copy_sighand(clone_flags, p);
if (retval)
goto bad_fork_cleanup_fs;
@@ -2615,8 +2615,6 @@ bad_fork_cleanup_sighand:
__cleanup_sighand(p->sighand);
bad_fork_cleanup_fs:
exit_fs(p); /* blocking */
-bad_fork_cleanup_files:
- exit_files(p); /* blocking */
bad_fork_cleanup_semundo:
exit_sem(p);
bad_fork_cleanup_security:
@@ -2627,6 +2625,8 @@ bad_fork_cleanup_perf:
perf_event_free_task(p);
bad_fork_sched_cancel_fork:
sched_cancel_fork(p);
+ /* ->release() of a file may need scx_fork_rwsem for write. */
+ exit_files(p); /* blocking */
bad_fork_cleanup_policy:
lockdep_free_task(p);
#ifdef CONFIG_NUMA
@@ -2705,6 +2705,10 @@ struct task_struct *create_io_thread(int (*fn)(void *), void *arg, int node)
.user_worker = 1,
};
+ /* A creator past its fatal signal or its coredump point gets no thread. */
+ if (current->flags & (PF_SIGNALED | PF_POSTCOREDUMP))
+ return ERR_PTR(-EINTR);
+
return copy_process(NULL, 0, node, &args);
}
@@ -3213,24 +3217,6 @@ static int unshare_fs(unsigned long unshare_flags, struct fs_struct **new_fsp)
}
/*
- * Unshare file descriptor table if it is being shared
- */
-static int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp)
-{
- struct files_struct *fd = current->files;
-
- if ((unshare_flags & CLONE_FILES) &&
- (fd && atomic_read(&fd->count) > 1)) {
- fd = dup_fd(fd, NULL);
- if (IS_ERR(fd))
- return PTR_ERR(fd);
- *new_fdp = fd;
- }
-
- return 0;
-}
-
-/*
* unshare allows a process to 'unshare' part of the process
* context which was originally shared using clone. copy_*
* functions used by kernel_clone() cannot be used here directly
@@ -3325,10 +3311,8 @@ int ksys_unshare(unsigned long unshare_flags)
if (new_fs)
new_fs = switch_fs_struct(new_fs);
- if (new_fd) {
- guard(task_lock)(current);
- swap(current->files, new_fd);
- }
+ if (new_fd)
+ switch_files_struct(current, no_free_ptr(new_fd));
if (new_cred) {
/* Install the new user namespace */
@@ -3361,30 +3345,6 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags)
return ksys_unshare(unshare_flags);
}
-/*
- * Helper to unshare the files of the current task.
- * We don't want to expose copy_files internals to
- * the exec layer of the kernel.
- */
-
-int unshare_files(void)
-{
- struct task_struct *task = current;
- struct files_struct *old, *copy = NULL;
- int error;
-
- error = unshare_fd(CLONE_FILES, &copy);
- if (error || !copy)
- return error;
-
- old = task->files;
- task_lock(task);
- task->files = copy;
- task_unlock(task);
- put_files_struct(old);
- return 0;
-}
-
static int sysctl_max_threads(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
diff --git a/kernel/kthread.c b/kernel/kthread.c
index a3f95c90456b..cc8cb5d3eab7 100644
--- a/kernel/kthread.c
+++ b/kernel/kthread.c
@@ -81,7 +81,7 @@ enum KTHREAD_BITS {
static inline struct kthread *to_kthread(struct task_struct *k)
{
- WARN_ON(!(k->flags & PF_KTHREAD));
+ WARN_ON(!(READ_ONCE(k->flags) & PF_KTHREAD));
return k->worker_private;
}
diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c
index d36afc58ee1d..8bc9edb40b78 100644
--- a/kernel/pid_namespace.c
+++ b/kernel/pid_namespace.c
@@ -238,9 +238,10 @@ void zap_pid_ns_processes(struct pid_namespace *pid_ns)
* kernel_wait4() will also block until our children traced from the
* parent namespace are detached and become EXIT_DEAD.
*/
+ /* Task work must not busy-loop the reaper, see signal_pending(). */
+ guard(no_notify_signal)();
do {
clear_thread_flag(TIF_SIGPENDING);
- clear_thread_flag(TIF_NOTIFY_SIGNAL);
rc = kernel_wait4(-1, NULL, __WALL, NULL);
} while (rc != -ECHILD);
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index d041645d9d17..4e9822a87aab 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -1227,6 +1227,12 @@ int ptrace_request(struct task_struct *child, long request,
case PTRACE_SETSIGMASK: {
sigset_t new_set;
+ /* A user worker only ever takes SIGKILL and SIGSTOP. */
+ if (child->flags & PF_USER_WORKER) {
+ ret = -EPERM;
+ break;
+ }
+
if (addr != sizeof(sigset_t)) {
ret = -EINVAL;
break;
diff --git a/kernel/signal.c b/kernel/signal.c
index d31ebcb6ed4d..e433de93b430 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -3170,6 +3170,10 @@ static void retarget_shared_pending(struct task_struct *tsk, sigset_t *which)
sigset_t retarget;
struct task_struct *t;
+ /* Nobody dequeues them in a dying group, see get_signal(). */
+ if (tsk->signal->flags & SIGNAL_GROUP_EXIT)
+ return;
+
sigandsets(&retarget, &tsk->signal->shared_pending.signal, which);
if (sigisemptyset(&retarget))
return;
@@ -3255,6 +3259,16 @@ long do_no_restart_syscall(struct restart_block *param)
static void __set_task_blocked(struct task_struct *tsk, const sigset_t *newset)
{
+ sigset_t floor, floored;
+
+ /* A user worker never unblocks anything but SIGKILL and SIGSTOP. */
+ if (unlikely(tsk->flags & PF_USER_WORKER)) {
+ siginitsetinv(&floor, SIG_KERNEL_ONLY_MASK);
+ sigorsets(&floored, newset, &floor);
+ WARN_ON_ONCE(!sigequalsets(&floored, newset));
+ newset = &floored;
+ }
+
if (task_sigpending(tsk) && !thread_group_empty(tsk)) {
sigset_t newblocked;
/* A set of now blocked but previously unblocked signals. */
diff --git a/kernel/tests/.kunitconfig b/kernel/tests/.kunitconfig
new file mode 100644
index 000000000000..b3d1206fd81a
--- /dev/null
+++ b/kernel/tests/.kunitconfig
@@ -0,0 +1,4 @@
+CONFIG_KUNIT=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_USER_NS_MAP_KUNIT_TEST=y
diff --git a/kernel/tests/user_ns_map_kunit.c b/kernel/tests/user_ns_map_kunit.c
new file mode 100644
index 000000000000..033dccc6a535
--- /dev/null
+++ b/kernel/tests/user_ns_map_kunit.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for user namespace map insertion and sorting.
+ */
+
+#define pr_fmt(fmt) "user_namespace: " fmt
+
+#include <kunit/test.h>
+#include <linux/user_namespace.h>
+
+#define NR_EXTENTS (UID_GID_MAP_MAX_BASE_EXTENTS + 5)
+
+static void user_ns_map_insert(struct kunit *test)
+{
+ struct uid_gid_map map;
+ struct uid_gid_extent extent;
+ int i, ret;
+
+ memset(&map, 0, sizeof(map));
+
+ /* Insert up to UID_GID_MAP_MAX_BASE_EXTENTS elements */
+ for (i = 0; i < UID_GID_MAP_MAX_BASE_EXTENTS; i++) {
+ extent.first = i * 10;
+ extent.lower_first = i * 100;
+ extent.count = 5;
+
+ ret = uid_gid_map_insert_extent(&map, &extent);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ }
+
+ KUNIT_EXPECT_EQ(test, map.nr_extents, UID_GID_MAP_MAX_BASE_EXTENTS);
+
+ /* Verify the elements ended up in the 'extent' array */
+ for (i = 0; i < UID_GID_MAP_MAX_BASE_EXTENTS; i++) {
+ KUNIT_EXPECT_EQ(test, map.extent[i].first, i * 10);
+ KUNIT_EXPECT_EQ(test, map.extent[i].lower_first, i * 100);
+ KUNIT_EXPECT_EQ(test, map.extent[i].count, 5);
+ }
+}
+
+static void user_ns_map_insert_extended(struct kunit *test)
+{
+ struct uid_gid_map map;
+ struct uid_gid_extent extent;
+ int i, ret;
+
+ memset(&map, 0, sizeof(map));
+
+ /* Insert more than UID_GID_MAP_MAX_BASE_EXTENTS elements */
+ for (i = 0; i < NR_EXTENTS; i++) {
+ int value = 9 - i;
+
+ extent.first = value * 10;
+ extent.lower_first = value * 100;
+ extent.count = 5;
+
+ ret = uid_gid_map_insert_extent(&map, &extent);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ }
+
+ KUNIT_EXPECT_EQ(test, map.nr_extents, NR_EXTENTS);
+
+ /* Now sort the map to set up reverse mapping */
+ ret = uid_gid_map_sort(&map);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, map.reverse);
+
+ /* Verify the elements are in 'forward' and that sorting is correct */
+ for (i = 0; i < map.nr_extents; i++) {
+ KUNIT_EXPECT_EQ(test, map.forward[i].first, i * 10);
+ KUNIT_EXPECT_EQ(test, map.forward[i].lower_first, i * 100);
+ KUNIT_EXPECT_EQ(test, map.forward[i].count, 5);
+
+ KUNIT_EXPECT_EQ(test, map.reverse[i].first, i * 10);
+ KUNIT_EXPECT_EQ(test, map.reverse[i].lower_first, i * 100);
+ KUNIT_EXPECT_EQ(test, map.reverse[i].count, 5);
+ }
+
+ kfree(map.forward);
+ kfree(map.reverse);
+}
+
+static struct kunit_case user_ns_map_test_cases[] = {
+ KUNIT_CASE(user_ns_map_insert),
+ KUNIT_CASE(user_ns_map_insert_extended),
+ {}
+};
+
+static struct kunit_suite user_ns_map_test_suite = {
+ .name = "user_ns_map",
+ .test_cases = user_ns_map_test_cases,
+};
+
+kunit_test_suite(user_ns_map_test_suite);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KUnit test for user namespace map insertion");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 0bed462e9b2a..1b23d819d398 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
+#include <kunit/visibility.h>
#include <linux/export.h>
#include <linux/nsproxy.h>
#include <linux/slab.h>
@@ -162,9 +163,6 @@ int create_user_ns(struct cred *new)
ns_tree_add(ns);
return 0;
fail_keyring:
-#ifdef CONFIG_PERSISTENT_KEYRINGS
- key_put(ns->persistent_keyring_register);
-#endif
ns_common_free(ns);
fail_free:
kmem_cache_free(user_ns_cachep, ns);
@@ -278,8 +276,8 @@ static int cmp_map_id(const void *k, const void *e)
* map_id_range_down_max - Find idmap via binary search in ordered idmap array.
* Can only be called if number of mappings exceeds UID_GID_MAP_MAX_BASE_EXTENTS.
*/
-static struct uid_gid_extent *
-map_id_range_down_max(unsigned extents, struct uid_gid_map *map, u32 id, u32 count)
+static const struct uid_gid_extent *
+map_id_range_down_max(unsigned extents, const struct uid_gid_map *map, u32 id, u32 count)
{
struct idmap_key key;
@@ -296,8 +294,8 @@ map_id_range_down_max(unsigned extents, struct uid_gid_map *map, u32 id, u32 cou
* Can only be called if number of mappings is equal or less than
* UID_GID_MAP_MAX_BASE_EXTENTS.
*/
-static struct uid_gid_extent *
-map_id_range_down_base(unsigned extents, struct uid_gid_map *map, u32 id, u32 count)
+static const struct uid_gid_extent *
+map_id_range_down_base(unsigned extents, const struct uid_gid_map *map, u32 id, u32 count)
{
unsigned idx;
u32 first, last, id2;
@@ -315,9 +313,9 @@ map_id_range_down_base(unsigned extents, struct uid_gid_map *map, u32 id, u32 co
return NULL;
}
-static u32 map_id_range_down(struct uid_gid_map *map, u32 id, u32 count)
+static u32 map_id_range_down(const struct uid_gid_map *map, u32 id, u32 count)
{
- struct uid_gid_extent *extent;
+ const struct uid_gid_extent *extent;
unsigned extents = map->nr_extents;
smp_rmb();
@@ -335,7 +333,7 @@ static u32 map_id_range_down(struct uid_gid_map *map, u32 id, u32 count)
return id;
}
-u32 map_id_down(struct uid_gid_map *map, u32 id)
+u32 map_id_down(const struct uid_gid_map *map, u32 id)
{
return map_id_range_down(map, id, 1);
}
@@ -345,8 +343,8 @@ u32 map_id_down(struct uid_gid_map *map, u32 id)
* Can only be called if number of mappings is equal or less than
* UID_GID_MAP_MAX_BASE_EXTENTS.
*/
-static struct uid_gid_extent *
-map_id_range_up_base(unsigned extents, struct uid_gid_map *map, u32 id, u32 count)
+static const struct uid_gid_extent *
+map_id_range_up_base(unsigned extents, const struct uid_gid_map *map, u32 id, u32 count)
{
unsigned idx;
u32 first, last, id2;
@@ -368,8 +366,8 @@ map_id_range_up_base(unsigned extents, struct uid_gid_map *map, u32 id, u32 coun
* map_id_up_max - Find idmap via binary search in ordered idmap array.
* Can only be called if number of mappings exceeds UID_GID_MAP_MAX_BASE_EXTENTS.
*/
-static struct uid_gid_extent *
-map_id_range_up_max(unsigned extents, struct uid_gid_map *map, u32 id, u32 count)
+static const struct uid_gid_extent *
+map_id_range_up_max(unsigned extents, const struct uid_gid_map *map, u32 id, u32 count)
{
struct idmap_key key;
@@ -381,9 +379,9 @@ map_id_range_up_max(unsigned extents, struct uid_gid_map *map, u32 id, u32 count
sizeof(struct uid_gid_extent), cmp_map_id);
}
-u32 map_id_range_up(struct uid_gid_map *map, u32 id, u32 count)
+u32 map_id_range_up(const struct uid_gid_map *map, u32 id, u32 count)
{
- struct uid_gid_extent *extent;
+ const struct uid_gid_extent *extent;
unsigned extents = map->nr_extents;
smp_rmb();
@@ -401,7 +399,7 @@ u32 map_id_range_up(struct uid_gid_map *map, u32 id, u32 count)
return id;
}
-u32 map_id_up(struct uid_gid_map *map, u32 id)
+u32 map_id_up(const struct uid_gid_map *map, u32 id)
{
return map_id_range_up(map, id, 1);
}
@@ -782,11 +780,13 @@ static bool mappings_overlap(struct uid_gid_map *new_map,
}
/*
- * insert_extent - Safely insert a new idmap extent into struct uid_gid_map.
+ * uid_gid_map_insert_extent - Safely insert a new idmap extent into
+ * struct uid_gid_map.
* Takes care to allocate a 4K block of memory if the number of mappings exceeds
* UID_GID_MAP_MAX_BASE_EXTENTS.
*/
-static int insert_extent(struct uid_gid_map *map, struct uid_gid_extent *extent)
+VISIBLE_IF_KUNIT int uid_gid_map_insert_extent(struct uid_gid_map *map,
+ struct uid_gid_extent *extent)
{
struct uid_gid_extent *dest;
@@ -809,15 +809,20 @@ static int insert_extent(struct uid_gid_map *map, struct uid_gid_extent *extent)
map->reverse = NULL;
}
- if (map->nr_extents < UID_GID_MAP_MAX_BASE_EXTENTS)
- dest = &map->extent[map->nr_extents];
+ /*
+ * nr_extents must be updated before the extent and forward arrays are
+ * accessed, otherwise KSAN will assert an out-of-bounds error.
+ */
+ map->nr_extents++;
+ if (map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
+ dest = &map->extent[map->nr_extents - 1];
else
- dest = &map->forward[map->nr_extents];
+ dest = &map->forward[map->nr_extents - 1];
*dest = *extent;
- map->nr_extents++;
return 0;
}
+EXPORT_SYMBOL_IF_KUNIT(uid_gid_map_insert_extent);
/* cmp function to sort() forward mappings */
static int cmp_extents_forward(const void *a, const void *b)
@@ -850,10 +855,10 @@ static int cmp_extents_reverse(const void *a, const void *b)
}
/*
- * sort_idmaps - Sorts an array of idmap entries.
+ * uid_gid_map_sort - Sorts an array of idmap entries.
* Can only be called if number of mappings exceeds UID_GID_MAP_MAX_BASE_EXTENTS.
*/
-static int sort_idmaps(struct uid_gid_map *map)
+VISIBLE_IF_KUNIT int uid_gid_map_sort(struct uid_gid_map *map)
{
if (map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
return 0;
@@ -874,6 +879,7 @@ static int sort_idmaps(struct uid_gid_map *map)
return 0;
}
+EXPORT_SYMBOL_IF_KUNIT(uid_gid_map_sort);
/**
* verify_root_map() - check the uid 0 mapping
@@ -1042,7 +1048,7 @@ static ssize_t map_write(struct file *file, const char __user *buf,
(next_line != NULL))
goto out;
- ret = insert_extent(&new_map, &extent);
+ ret = uid_gid_map_insert_extent(&new_map, &extent);
if (ret < 0)
goto out;
ret = -EINVAL;
@@ -1086,7 +1092,7 @@ static ssize_t map_write(struct file *file, const char __user *buf,
* If we want to use binary search for lookup, this clones the extent
* array and sorts both copies.
*/
- ret = sort_idmaps(&new_map);
+ ret = uid_gid_map_sort(&new_map);
if (ret < 0)
goto out;
diff --git a/kernel/utsname.c b/kernel/utsname.c
index ebbfc578a9d3..1ebf87e24607 100644
--- a/kernel/utsname.c
+++ b/kernel/utsname.c
@@ -81,7 +81,6 @@ struct uts_namespace *copy_utsname(u64 flags,
{
struct uts_namespace *new_ns;
- BUG_ON(!old_ns);
get_uts_ns(old_ns);
if (!(flags & CLONE_NEWUTS))