Skip to content

Commit a8136af

Browse files
braunergregkh
authored andcommitted
acct: perform last write from workqueue
commit 56d5f3e upstream. In [1] it was reported that the acct(2) system call can be used to trigger NULL deref in cases where it is set to write to a file that triggers an internal lookup. This can e.g., happen when pointing acc(2) to /sys/power/resume. At the point the where the write to this file happens the calling task has already exited and called exit_fs(). A lookup will thus trigger a NULL-deref when accessing current->fs. Reorganize the code so that the the final write happens from the workqueue but with the caller's credentials. This preserves the (strange) permission model and has almost no regression risk. This api should stop to exist though. Link: https://lore.kernel.org/r/[email protected] [1] Link: https://lore.kernel.org/r/[email protected] Fixes: 1da177e ("Linux-2.6.12-rc2") Reported-by: Zicheng Qu <[email protected]> Cc: [email protected] Signed-off-by: Christian Brauner <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent a5b068f commit a8136af

File tree

1 file changed

+70
-50
lines changed

1 file changed

+70
-50
lines changed

kernel/acct.c

Lines changed: 70 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -103,48 +103,50 @@ struct bsd_acct_struct {
103103
atomic_long_t count;
104104
struct rcu_head rcu;
105105
struct mutex lock;
106-
int active;
106+
bool active;
107+
bool check_space;
107108
unsigned long needcheck;
108109
struct file *file;
109110
struct pid_namespace *ns;
110111
struct work_struct work;
111112
struct completion done;
113+
acct_t ac;
112114
};
113115

114-
static void do_acct_process(struct bsd_acct_struct *acct);
116+
static void fill_ac(struct bsd_acct_struct *acct);
117+
static void acct_write_process(struct bsd_acct_struct *acct);
115118

116119
/*
117120
* Check the amount of free space and suspend/resume accordingly.
118121
*/
119-
static int check_free_space(struct bsd_acct_struct *acct)
122+
static bool check_free_space(struct bsd_acct_struct *acct)
120123
{
121124
struct kstatfs sbuf;
122125

123-
if (time_is_after_jiffies(acct->needcheck))
124-
goto out;
126+
if (!acct->check_space)
127+
return acct->active;
125128

126129
/* May block */
127130
if (vfs_statfs(&acct->file->f_path, &sbuf))
128-
goto out;
131+
return acct->active;
129132

130133
if (acct->active) {
131134
u64 suspend = sbuf.f_blocks * SUSPEND;
132135
do_div(suspend, 100);
133136
if (sbuf.f_bavail <= suspend) {
134-
acct->active = 0;
137+
acct->active = false;
135138
pr_info("Process accounting paused\n");
136139
}
137140
} else {
138141
u64 resume = sbuf.f_blocks * RESUME;
139142
do_div(resume, 100);
140143
if (sbuf.f_bavail >= resume) {
141-
acct->active = 1;
144+
acct->active = true;
142145
pr_info("Process accounting resumed\n");
143146
}
144147
}
145148

146149
acct->needcheck = jiffies + ACCT_TIMEOUT*HZ;
147-
out:
148150
return acct->active;
149151
}
150152

@@ -189,7 +191,11 @@ static void acct_pin_kill(struct fs_pin *pin)
189191
{
190192
struct bsd_acct_struct *acct = to_acct(pin);
191193
mutex_lock(&acct->lock);
192-
do_acct_process(acct);
194+
/*
195+
* Fill the accounting struct with the exiting task's info
196+
* before punting to the workqueue.
197+
*/
198+
fill_ac(acct);
193199
schedule_work(&acct->work);
194200
wait_for_completion(&acct->done);
195201
cmpxchg(&acct->ns->bacct, pin, NULL);
@@ -202,6 +208,9 @@ static void close_work(struct work_struct *work)
202208
{
203209
struct bsd_acct_struct *acct = container_of(work, struct bsd_acct_struct, work);
204210
struct file *file = acct->file;
211+
212+
/* We were fired by acct_pin_kill() which holds acct->lock. */
213+
acct_write_process(acct);
205214
if (file->f_op->flush)
206215
file->f_op->flush(file, NULL);
207216
__fput_sync(file);
@@ -430,13 +439,27 @@ static u32 encode_float(u64 value)
430439
* do_exit() or when switching to a different output file.
431440
*/
432441

433-
static void fill_ac(acct_t *ac)
442+
static void fill_ac(struct bsd_acct_struct *acct)
434443
{
435444
struct pacct_struct *pacct = &current->signal->pacct;
445+
struct file *file = acct->file;
446+
acct_t *ac = &acct->ac;
436447
u64 elapsed, run_time;
437448
time64_t btime;
438449
struct tty_struct *tty;
439450

451+
lockdep_assert_held(&acct->lock);
452+
453+
if (time_is_after_jiffies(acct->needcheck)) {
454+
acct->check_space = false;
455+
456+
/* Don't fill in @ac if nothing will be written. */
457+
if (!acct->active)
458+
return;
459+
} else {
460+
acct->check_space = true;
461+
}
462+
440463
/*
441464
* Fill the accounting struct with the needed info as recorded
442465
* by the different kernel functions.
@@ -484,64 +507,61 @@ static void fill_ac(acct_t *ac)
484507
ac->ac_majflt = encode_comp_t(pacct->ac_majflt);
485508
ac->ac_exitcode = pacct->ac_exitcode;
486509
spin_unlock_irq(&current->sighand->siglock);
487-
}
488-
/*
489-
* do_acct_process does all actual work. Caller holds the reference to file.
490-
*/
491-
static void do_acct_process(struct bsd_acct_struct *acct)
492-
{
493-
acct_t ac;
494-
unsigned long flim;
495-
const struct cred *orig_cred;
496-
struct file *file = acct->file;
497-
498-
/*
499-
* Accounting records are not subject to resource limits.
500-
*/
501-
flim = rlimit(RLIMIT_FSIZE);
502-
current->signal->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
503-
/* Perform file operations on behalf of whoever enabled accounting */
504-
orig_cred = override_creds(file->f_cred);
505510

506-
/*
507-
* First check to see if there is enough free_space to continue
508-
* the process accounting system.
509-
*/
510-
if (!check_free_space(acct))
511-
goto out;
512-
513-
fill_ac(&ac);
514511
/* we really need to bite the bullet and change layout */
515-
ac.ac_uid = from_kuid_munged(file->f_cred->user_ns, orig_cred->uid);
516-
ac.ac_gid = from_kgid_munged(file->f_cred->user_ns, orig_cred->gid);
512+
ac->ac_uid = from_kuid_munged(file->f_cred->user_ns, current_uid());
513+
ac->ac_gid = from_kgid_munged(file->f_cred->user_ns, current_gid());
517514
#if ACCT_VERSION == 1 || ACCT_VERSION == 2
518515
/* backward-compatible 16 bit fields */
519-
ac.ac_uid16 = ac.ac_uid;
520-
ac.ac_gid16 = ac.ac_gid;
516+
ac->ac_uid16 = ac->ac_uid;
517+
ac->ac_gid16 = ac->ac_gid;
521518
#elif ACCT_VERSION == 3
522519
{
523520
struct pid_namespace *ns = acct->ns;
524521

525-
ac.ac_pid = task_tgid_nr_ns(current, ns);
522+
ac->ac_pid = task_tgid_nr_ns(current, ns);
526523
rcu_read_lock();
527-
ac.ac_ppid = task_tgid_nr_ns(rcu_dereference(current->real_parent),
528-
ns);
524+
ac->ac_ppid = task_tgid_nr_ns(rcu_dereference(current->real_parent), ns);
529525
rcu_read_unlock();
530526
}
531527
#endif
528+
}
529+
530+
static void acct_write_process(struct bsd_acct_struct *acct)
531+
{
532+
struct file *file = acct->file;
533+
const struct cred *cred;
534+
acct_t *ac = &acct->ac;
535+
536+
/* Perform file operations on behalf of whoever enabled accounting */
537+
cred = override_creds(file->f_cred);
538+
532539
/*
533-
* Get freeze protection. If the fs is frozen, just skip the write
534-
* as we could deadlock the system otherwise.
540+
* First check to see if there is enough free_space to continue
541+
* the process accounting system. Then get freeze protection. If
542+
* the fs is frozen, just skip the write as we could deadlock
543+
* the system otherwise.
535544
*/
536-
if (file_start_write_trylock(file)) {
545+
if (check_free_space(acct) && file_start_write_trylock(file)) {
537546
/* it's been opened O_APPEND, so position is irrelevant */
538547
loff_t pos = 0;
539-
__kernel_write(file, &ac, sizeof(acct_t), &pos);
548+
__kernel_write(file, ac, sizeof(acct_t), &pos);
540549
file_end_write(file);
541550
}
542-
out:
551+
552+
revert_creds(cred);
553+
}
554+
555+
static void do_acct_process(struct bsd_acct_struct *acct)
556+
{
557+
unsigned long flim;
558+
559+
/* Accounting records are not subject to resource limits. */
560+
flim = rlimit(RLIMIT_FSIZE);
561+
current->signal->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
562+
fill_ac(acct);
563+
acct_write_process(acct);
543564
current->signal->rlim[RLIMIT_FSIZE].rlim_cur = flim;
544-
revert_creds(orig_cred);
545565
}
546566

547567
/**

0 commit comments

Comments
 (0)