Skip to content

Commit 8d909b2

Browse files
jognesspmladek
authored andcommitted
printk: syslog: close window between wait and read
Syslog's SYSLOG_ACTION_READ is supposed to block until the next syslog record can be read, and then it should read that record. However, because @syslog_lock is not held between waking up and reading the record, another reader could read the record first, thus causing SYSLOG_ACTION_READ to return with a value of 0, never having read _anything_. By holding @syslog_lock between waking up and reading, it can be guaranteed that SYSLOG_ACTION_READ blocks until it successfully reads a syslog record (or a real error occurs). Signed-off-by: John Ogness <[email protected]> Reviewed-by: Petr Mladek <[email protected]> Signed-off-by: Petr Mladek <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent b371cbb commit 8d909b2

File tree

1 file changed

+36
-19
lines changed

1 file changed

+36
-19
lines changed

kernel/printk/printk.c

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,28 +1480,50 @@ static u64 find_first_fitting_seq(u64 start_seq, u64 max_seq, size_t size,
14801480
return seq;
14811481
}
14821482

1483+
/* The caller is responsible for making sure @size is greater than 0. */
14831484
static int syslog_print(char __user *buf, int size)
14841485
{
14851486
struct printk_info info;
14861487
struct printk_record r;
14871488
char *text;
14881489
int len = 0;
1490+
u64 seq;
14891491

14901492
text = kmalloc(CONSOLE_LOG_MAX, GFP_KERNEL);
14911493
if (!text)
14921494
return -ENOMEM;
14931495

14941496
prb_rec_init_rd(&r, &info, text, CONSOLE_LOG_MAX);
14951497

1496-
while (size > 0) {
1498+
mutex_lock(&syslog_lock);
1499+
1500+
/*
1501+
* Wait for the @syslog_seq record to be available. @syslog_seq may
1502+
* change while waiting.
1503+
*/
1504+
do {
1505+
seq = syslog_seq;
1506+
1507+
mutex_unlock(&syslog_lock);
1508+
len = wait_event_interruptible(log_wait, prb_read_valid(prb, seq, NULL));
1509+
mutex_lock(&syslog_lock);
1510+
1511+
if (len)
1512+
goto out;
1513+
} while (syslog_seq != seq);
1514+
1515+
/*
1516+
* Copy records that fit into the buffer. The above cycle makes sure
1517+
* that the first record is always available.
1518+
*/
1519+
do {
14971520
size_t n;
14981521
size_t skip;
1522+
int err;
14991523

1500-
mutex_lock(&syslog_lock);
1501-
if (!prb_read_valid(prb, syslog_seq, &r)) {
1502-
mutex_unlock(&syslog_lock);
1524+
if (!prb_read_valid(prb, syslog_seq, &r))
15031525
break;
1504-
}
1526+
15051527
if (r.info->seq != syslog_seq) {
15061528
/* message is gone, move to next valid one */
15071529
syslog_seq = r.info->seq;
@@ -1528,12 +1550,15 @@ static int syslog_print(char __user *buf, int size)
15281550
syslog_partial += n;
15291551
} else
15301552
n = 0;
1531-
mutex_unlock(&syslog_lock);
15321553

15331554
if (!n)
15341555
break;
15351556

1536-
if (copy_to_user(buf, text + skip, n)) {
1557+
mutex_unlock(&syslog_lock);
1558+
err = copy_to_user(buf, text + skip, n);
1559+
mutex_lock(&syslog_lock);
1560+
1561+
if (err) {
15371562
if (!len)
15381563
len = -EFAULT;
15391564
break;
@@ -1542,8 +1567,9 @@ static int syslog_print(char __user *buf, int size)
15421567
len += n;
15431568
size -= n;
15441569
buf += n;
1545-
}
1546-
1570+
} while (size);
1571+
out:
1572+
mutex_unlock(&syslog_lock);
15471573
kfree(text);
15481574
return len;
15491575
}
@@ -1614,7 +1640,6 @@ int do_syslog(int type, char __user *buf, int len, int source)
16141640
bool clear = false;
16151641
static int saved_console_loglevel = LOGLEVEL_DEFAULT;
16161642
int error;
1617-
u64 seq;
16181643

16191644
error = check_syslog_permissions(type, source);
16201645
if (error)
@@ -1632,15 +1657,6 @@ int do_syslog(int type, char __user *buf, int len, int source)
16321657
return 0;
16331658
if (!access_ok(buf, len))
16341659
return -EFAULT;
1635-
1636-
/* Get a consistent copy of @syslog_seq. */
1637-
mutex_lock(&syslog_lock);
1638-
seq = syslog_seq;
1639-
mutex_unlock(&syslog_lock);
1640-
1641-
error = wait_event_interruptible(log_wait, prb_read_valid(prb, seq, NULL));
1642-
if (error)
1643-
return error;
16441660
error = syslog_print(buf, len);
16451661
break;
16461662
/* Read/clear last kernel messages */
@@ -1707,6 +1723,7 @@ int do_syslog(int type, char __user *buf, int len, int source)
17071723
} else {
17081724
bool time = syslog_partial ? syslog_time : printk_time;
17091725
unsigned int line_count;
1726+
u64 seq;
17101727

17111728
prb_for_each_info(syslog_seq, prb, seq, &info,
17121729
&line_count) {

0 commit comments

Comments
 (0)