Unverified Commit 3a9f4565 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!15622 Fix UAF in acct(2)

Merge Pull Request from: @ci-robot 
 
PR sync from: Liu Kai <liukai284@huawei.com>
https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/52QEC4357FQFN6MZG4SN3IEFWDCNO2QJ/ 
Fix UAF in acct(2)

Christian Brauner (2):
  acct: perform last write from workqueue
  acct: block access to kernel internal filesystems

 
https://gitee.com/src-openeuler/kernel/issues/IBSW0O 
 
Link:https://gitee.com/openeuler/kernel/pulls/15622

 

Reviewed-by: default avatarZhang Peng <zhangpeng362@huawei.com>
Signed-off-by: default avatarZhang Peng <zhangpeng362@huawei.com>
parents 5e5ca013 3c811eb4
Loading
Loading
Loading
Loading
+84 −50
Original line number Diff line number Diff line
@@ -104,48 +104,50 @@ struct bsd_acct_struct {
	atomic_long_t		count;
	struct rcu_head		rcu;
	struct mutex		lock;
	int			active;
	bool			active;
	bool			check_space;
	unsigned long		needcheck;
	struct file		*file;
	struct pid_namespace	*ns;
	struct work_struct	work;
	struct completion	done;
	acct_t			ac;
};

static void do_acct_process(struct bsd_acct_struct *acct);
static void fill_ac(struct bsd_acct_struct *acct);
static void acct_write_process(struct bsd_acct_struct *acct);

/*
 * Check the amount of free space and suspend/resume accordingly.
 */
static int check_free_space(struct bsd_acct_struct *acct)
static bool check_free_space(struct bsd_acct_struct *acct)
{
	struct kstatfs sbuf;

	if (time_is_after_jiffies(acct->needcheck))
		goto out;
	if (!acct->check_space)
		return acct->active;

	/* May block */
	if (vfs_statfs(&acct->file->f_path, &sbuf))
		goto out;
		return acct->active;

	if (acct->active) {
		u64 suspend = sbuf.f_blocks * SUSPEND;
		do_div(suspend, 100);
		if (sbuf.f_bavail <= suspend) {
			acct->active = 0;
			acct->active = false;
			pr_info("Process accounting paused\n");
		}
	} else {
		u64 resume = sbuf.f_blocks * RESUME;
		do_div(resume, 100);
		if (sbuf.f_bavail >= resume) {
			acct->active = 1;
			acct->active = true;
			pr_info("Process accounting resumed\n");
		}
	}

	acct->needcheck = jiffies + ACCT_TIMEOUT*HZ;
out:
	return acct->active;
}

@@ -190,7 +192,11 @@ static void acct_pin_kill(struct fs_pin *pin)
{
	struct bsd_acct_struct *acct = to_acct(pin);
	mutex_lock(&acct->lock);
	do_acct_process(acct);
	/*
	 * Fill the accounting struct with the exiting task's info
	 * before punting to the workqueue.
	 */
	fill_ac(acct);
	schedule_work(&acct->work);
	wait_for_completion(&acct->done);
	cmpxchg(&acct->ns->bacct, pin, NULL);
@@ -203,6 +209,9 @@ static void close_work(struct work_struct *work)
{
	struct bsd_acct_struct *acct = container_of(work, struct bsd_acct_struct, work);
	struct file *file = acct->file;

	/* We were fired by acct_pin_kill() which holds acct->lock. */
	acct_write_process(acct);
	if (file->f_op->flush)
		file->f_op->flush(file, NULL);
	__fput_sync(file);
@@ -235,6 +244,20 @@ static int acct_on(struct filename *pathname)
		return -EACCES;
	}

	/* Exclude kernel kernel internal filesystems. */
	if (file_inode(file)->i_sb->s_flags & (SB_NOUSER | SB_KERNMOUNT)) {
		kfree(acct);
		filp_close(file, NULL);
		return -EINVAL;
	}

	/* Exclude procfs and sysfs. */
	if (file_inode(file)->i_sb->s_iflags & SB_I_USERNS_VISIBLE) {
		kfree(acct);
		filp_close(file, NULL);
		return -EINVAL;
	}

	if (!(file->f_mode & FMODE_CAN_WRITE)) {
		kfree(acct);
		filp_close(file, NULL);
@@ -431,13 +454,27 @@ static u32 encode_float(u64 value)
 *  do_exit() or when switching to a different output file.
 */

static void fill_ac(acct_t *ac)
static void fill_ac(struct bsd_acct_struct *acct)
{
	struct pacct_struct *pacct = &current->signal->pacct;
	struct file *file = acct->file;
	acct_t *ac = &acct->ac;
	u64 elapsed, run_time;
	time64_t btime;
	struct tty_struct *tty;

	lockdep_assert_held(&acct->lock);

	if (time_is_after_jiffies(acct->needcheck)) {
		acct->check_space = false;

		/* Don't fill in @ac if nothing will be written. */
		if (!acct->active)
			return;
	} else {
		acct->check_space = true;
	}

	/*
	 * Fill the accounting struct with the needed info as recorded
	 * by the different kernel functions.
@@ -485,64 +522,61 @@ static void fill_ac(acct_t *ac)
	ac->ac_majflt = encode_comp_t(pacct->ac_majflt);
	ac->ac_exitcode = pacct->ac_exitcode;
	spin_unlock_irq(&current->sighand->siglock);
}
/*
 *  do_acct_process does all actual work. Caller holds the reference to file.
 */
static void do_acct_process(struct bsd_acct_struct *acct)
{
	acct_t ac;
	unsigned long flim;
	const struct cred *orig_cred;
	struct file *file = acct->file;

	/*
	 * Accounting records are not subject to resource limits.
	 */
	flim = rlimit(RLIMIT_FSIZE);
	current->signal->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
	/* Perform file operations on behalf of whoever enabled accounting */
	orig_cred = override_creds(file->f_cred);

	/*
	 * First check to see if there is enough free_space to continue
	 * the process accounting system.
	 */
	if (!check_free_space(acct))
		goto out;

	fill_ac(&ac);
	/* we really need to bite the bullet and change layout */
	ac.ac_uid = from_kuid_munged(file->f_cred->user_ns, orig_cred->uid);
	ac.ac_gid = from_kgid_munged(file->f_cred->user_ns, orig_cred->gid);
	ac->ac_uid = from_kuid_munged(file->f_cred->user_ns, current_uid());
	ac->ac_gid = from_kgid_munged(file->f_cred->user_ns, current_gid());
#if ACCT_VERSION == 1 || ACCT_VERSION == 2
	/* backward-compatible 16 bit fields */
	ac.ac_uid16 = ac.ac_uid;
	ac.ac_gid16 = ac.ac_gid;
	ac->ac_uid16 = ac->ac_uid;
	ac->ac_gid16 = ac->ac_gid;
#elif ACCT_VERSION == 3
	{
		struct pid_namespace *ns = acct->ns;

		ac.ac_pid = task_tgid_nr_ns(current, ns);
		ac->ac_pid = task_tgid_nr_ns(current, ns);
		rcu_read_lock();
		ac.ac_ppid = task_tgid_nr_ns(rcu_dereference(current->real_parent),
					     ns);
		ac->ac_ppid = task_tgid_nr_ns(rcu_dereference(current->real_parent), ns);
		rcu_read_unlock();
	}
#endif
}

static void acct_write_process(struct bsd_acct_struct *acct)
{
	struct file *file = acct->file;
	const struct cred *cred;
	acct_t *ac = &acct->ac;

	/* Perform file operations on behalf of whoever enabled accounting */
	cred = override_creds(file->f_cred);

	/*
	 * Get freeze protection. If the fs is frozen, just skip the write
	 * as we could deadlock the system otherwise.
	 * First check to see if there is enough free_space to continue
	 * the process accounting system. Then get freeze protection. If
	 * the fs is frozen, just skip the write as we could deadlock
	 * the system otherwise.
	 */
	if (file_start_write_trylock(file)) {
	if (check_free_space(acct) && file_start_write_trylock(file)) {
		/* it's been opened O_APPEND, so position is irrelevant */
		loff_t pos = 0;
		__kernel_write(file, &ac, sizeof(acct_t), &pos);
		__kernel_write(file, ac, sizeof(acct_t), &pos);
		file_end_write(file);
	}
out:

	revert_creds(cred);
}

static void do_acct_process(struct bsd_acct_struct *acct)
{
	unsigned long flim;

	/* Accounting records are not subject to resource limits. */
	flim = rlimit(RLIMIT_FSIZE);
	current->signal->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
	fill_ac(acct);
	acct_write_process(acct);
	current->signal->rlim[RLIMIT_FSIZE].rlim_cur = flim;
	revert_creds(orig_cred);
}

/**