cregit-Linux how code gets into the kernel

Release 4.15 kernel/printk/printk.c

Directory: kernel/printk
/*
 *  linux/kernel/printk.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 * Modified to make sys_syslog() more flexible: added commands to
 * return the last 4k of kernel messages, regardless of whether
 * they've been read or not.  Added option to suppress kernel printk's
 * to the console.  Added hook for sending the console messages
 * elsewhere, in preparation for a serial line console (someday).
 * Ted Ts'o, 2/11/93.
 * Modified for sysctl support, 1/8/97, Chris Horn.
 * Fixed SMP synchronization, 08/08/99, Manfred Spraul
 *     manfred@colorfullife.com
 * Rewrote bits to get rid of console_lock
 *      01Mar01 Andrew Morton
 */

#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/console.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/nmi.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <linux/smp.h>
#include <linux/security.h>
#include <linux/bootmem.h>
#include <linux/memblock.h>
#include <linux/syscalls.h>
#include <linux/crash_core.h>
#include <linux/kdb.h>
#include <linux/ratelimit.h>
#include <linux/kmsg_dump.h>
#include <linux/syslog.h>
#include <linux/cpu.h>
#include <linux/notifier.h>
#include <linux/rculist.h>
#include <linux/poll.h>
#include <linux/irq_work.h>
#include <linux/utsname.h>
#include <linux/ctype.h>
#include <linux/uio.h>
#include <linux/sched/clock.h>
#include <linux/sched/debug.h>
#include <linux/sched/task_stack.h>

#include <linux/uaccess.h>
#include <asm/sections.h>


#define CREATE_TRACE_POINTS
#include <trace/events/printk.h>

#include "console_cmdline.h"
#include "braille.h"
#include "internal.h"


int console_printk[4] = {
	CONSOLE_LOGLEVEL_DEFAULT,	/* console_loglevel */
	MESSAGE_LOGLEVEL_DEFAULT,	/* default_message_loglevel */
	CONSOLE_LOGLEVEL_MIN,		/* minimum_console_loglevel */
	CONSOLE_LOGLEVEL_DEFAULT,	/* default_console_loglevel */
};

/*
 * Low level drivers may need that to know if they can schedule in
 * their unblank() callback or not. So let's export it.
 */

int oops_in_progress;

EXPORT_SYMBOL(oops_in_progress);

/*
 * console_sem protects the console_drivers list, and also
 * provides serialisation for access to the entire console
 * driver system.
 */
static DEFINE_SEMAPHORE(console_sem);

struct console *console_drivers;

EXPORT_SYMBOL_GPL(console_drivers);

#ifdef CONFIG_LOCKDEP

static struct lockdep_map console_lock_dep_map = {
	.name = "console_lock"
};
#endif


enum devkmsg_log_bits {
	
__DEVKMSG_LOG_BIT_ON = 0,
	
__DEVKMSG_LOG_BIT_OFF,
	
__DEVKMSG_LOG_BIT_LOCK,
};


enum devkmsg_log_masks {
	
DEVKMSG_LOG_MASK_ON             = BIT(__DEVKMSG_LOG_BIT_ON),
	
DEVKMSG_LOG_MASK_OFF            = BIT(__DEVKMSG_LOG_BIT_OFF),
	
DEVKMSG_LOG_MASK_LOCK           = BIT(__DEVKMSG_LOG_BIT_LOCK),
};

/* Keep both the 'on' and 'off' bits clear, i.e. ratelimit by default: */

#define DEVKMSG_LOG_MASK_DEFAULT	0


static unsigned int __read_mostly devkmsg_log = DEVKMSG_LOG_MASK_DEFAULT;


static int __control_devkmsg(char *str) { if (!str) return -EINVAL; if (!strncmp(str, "on", 2)) { devkmsg_log = DEVKMSG_LOG_MASK_ON; return 2; } else if (!strncmp(str, "off", 3)) { devkmsg_log = DEVKMSG_LOG_MASK_OFF; return 3; } else if (!strncmp(str, "ratelimit", 9)) { devkmsg_log = DEVKMSG_LOG_MASK_DEFAULT; return 9; } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Borislav Petkov88100.00%1100.00%
Total88100.00%1100.00%


static int __init control_devkmsg(char *str) { if (__control_devkmsg(str) < 0) return 1; /* * Set sysctl string accordingly: */ if (devkmsg_log == DEVKMSG_LOG_MASK_ON) { memset(devkmsg_log_str, 0, DEVKMSG_STR_MAX_SIZE); strncpy(devkmsg_log_str, "on", 2); } else if (devkmsg_log == DEVKMSG_LOG_MASK_OFF) { memset(devkmsg_log_str, 0, DEVKMSG_STR_MAX_SIZE); strncpy(devkmsg_log_str, "off", 3); } /* else "ratelimit" which is set by default. */ /* * Sysctl cannot change it anymore. The kernel command line setting of * this parameter is to force the setting to be permanent throughout the * runtime of the system. This is a precation measure against userspace * trying to be a smarta** and attempting to change it up on us. */ devkmsg_log |= DEVKMSG_LOG_MASK_LOCK; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Borislav Petkov86100.00%1100.00%
Total86100.00%1100.00%

__setup("printk.devkmsg=", control_devkmsg); char devkmsg_log_str[DEVKMSG_STR_MAX_SIZE] = "ratelimit";
int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { char old_str[DEVKMSG_STR_MAX_SIZE]; unsigned int old; int err; if (write) { if (devkmsg_log & DEVKMSG_LOG_MASK_LOCK) return -EINVAL; old = devkmsg_log; strncpy(old_str, devkmsg_log_str, DEVKMSG_STR_MAX_SIZE); } err = proc_dostring(table, write, buffer, lenp, ppos); if (err) return err; if (write) { err = __control_devkmsg(devkmsg_log_str); /* * Do not accept an unknown string OR a known string with * trailing crap... */ if (err < 0 || (err + 1 != *lenp)) { /* ... and restore old setting. */ devkmsg_log = old; strncpy(devkmsg_log_str, old_str, DEVKMSG_STR_MAX_SIZE); return -EINVAL; } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Borislav Petkov142100.00%1100.00%
Total142100.00%1100.00%

/* * Number of registered extended console drivers. * * If extended consoles are present, in-kernel cont reassembly is disabled * and each fragment is stored as a separate log entry with proper * continuation flag so that every emitted message has full metadata. This * doesn't change the result for regular consoles or /proc/kmsg. For * /dev/kmsg, as long as the reader concatenates messages according to * consecutive continuation flags, the end result should be the same too. */ static int nr_ext_console_drivers; /* * Helper macros to handle lockdep when locking/unlocking console_sem. We use * macros instead of functions so that _RET_IP_ contains useful information. */ #define down_console_sem() do { \ down(&console_sem);\ mutex_acquire(&console_lock_dep_map, 0, 0, _RET_IP_);\ } while (0)
static int __down_trylock_console_sem(unsigned long ip) { int lock_failed; unsigned long flags; /* * Here and in __up_console_sem() we need to be in safe mode, * because spindump/WARN/etc from under console ->lock will * deadlock in printk()->down_trylock_console_sem() otherwise. */ printk_safe_enter_irqsave(flags); lock_failed = down_trylock(&console_sem); printk_safe_exit_irqrestore(flags); if (lock_failed) return 1; mutex_acquire(&console_lock_dep_map, 0, 1, ip); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jan Kara3458.62%150.00%
Sergey Senozhatsky2441.38%150.00%
Total58100.00%2100.00%

#define down_trylock_console_sem() __down_trylock_console_sem(_RET_IP_)
static void __up_console_sem(unsigned long ip) { unsigned long flags; mutex_release(&console_lock_dep_map, 1, ip); printk_safe_enter_irqsave(flags); up(&console_sem); printk_safe_exit_irqrestore(flags); }

Contributors

PersonTokensPropCommitsCommitProp
Sergey Senozhatsky40100.00%1100.00%
Total40100.00%1100.00%

#define up_console_sem() __up_console_sem(_RET_IP_) /* * This is used for debugging the mess that is the VT code by * keeping track if we have the console semaphore held. It's * definitely not the perfect debug tool (we don't know if _WE_ * hold it and are racing, but it helps tracking those weird code * paths in the console code where we end up in places I want * locked without the console sempahore held). */ static int console_locked, console_suspended; /* * If exclusive_console is non-NULL then only this console is to be printed to. */ static struct console *exclusive_console; /* * Array of consoles built from command line options (console=) */ #define MAX_CMDLINECONSOLES 8 static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES]; static int preferred_console = -1; int console_set_on_cmdline; EXPORT_SYMBOL(console_set_on_cmdline); /* Flag: console code may call schedule() */ static int console_may_schedule; /* * The printk log buffer consists of a chain of concatenated variable * length records. Every record starts with a record header, containing * the overall length of the record. * * The heads to the first and last entry in the buffer, as well as the * sequence numbers of these entries are maintained when messages are * stored. * * If the heads indicate available messages, the length in the header * tells the start next message. A length == 0 for the next message * indicates a wrap-around to the beginning of the buffer. * * Every record carries the monotonic timestamp in microseconds, as well as * the standard userspace syslog level and syslog facility. The usual * kernel messages use LOG_KERN; userspace-injected messages always carry * a matching syslog facility, by default LOG_USER. The origin of every * message can be reliably determined that way. * * The human readable log message directly follows the message header. The * length of the message text is stored in the header, the stored message * is not terminated. * * Optionally, a message can carry a dictionary of properties (key/value pairs), * to provide userspace with a machine-readable message context. * * Examples for well-defined, commonly used property names are: * DEVICE=b12:8 device identifier * b12:8 block dev_t * c127:3 char dev_t * n8 netdev ifindex * +sound:card0 subsystem:devname * SUBSYSTEM=pci driver-core subsystem name * * Valid characters in property names are [a-zA-Z0-9.-_]. The plain text value * follows directly after a '=' character. Every property is terminated by * a '\0' character. The last property is not terminated. * * Example of a message structure: * 0000 ff 8f 00 00 00 00 00 00 monotonic time in nsec * 0008 34 00 record is 52 bytes long * 000a 0b 00 text is 11 bytes long * 000c 1f 00 dictionary is 23 bytes long * 000e 03 00 LOG_KERN (facility) LOG_ERR (level) * 0010 69 74 27 73 20 61 20 6c "it's a l" * 69 6e 65 "ine" * 001b 44 45 56 49 43 "DEVIC" * 45 3d 62 38 3a 32 00 44 "E=b8:2\0D" * 52 49 56 45 52 3d 62 75 "RIVER=bu" * 67 "g" * 0032 00 00 00 padding to next message header * * The 'struct printk_log' buffer header must never be directly exported to * userspace, it is a kernel-private implementation detail that might * need to be changed in the future, when the requirements change. * * /dev/kmsg exports the structured data in the following line format: * "<level>,<sequnum>,<timestamp>,<contflag>[,additional_values, ... ];<message text>\n" * * Users of the export format should ignore possible additional values * separated by ',', and find the message after the ';' character. * * The optional key/value pairs are attached as continuation lines starting * with a space character and terminated by a newline. All possible * non-prinatable characters are escaped in the "\xff" notation. */ enum log_flags { LOG_NOCONS = 1, /* already flushed, do not print to console */ LOG_NEWLINE = 2, /* text ended with a newline */ LOG_PREFIX = 4, /* text started with a prefix */ LOG_CONT = 8, /* text is a fragment of a continuation line */ }; struct printk_log { u64 ts_nsec; /* timestamp in nanoseconds */ u16 len; /* length of entire record */ u16 text_len; /* length of text buffer */ u16 dict_len; /* length of dictionary buffer */ u8 facility; /* syslog facility */ u8 flags:5; /* internal record flags */ u8 level:3; /* syslog level */ } #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS __packed __aligned(4) #endif ; /* * The logbuf_lock protects kmsg buffer, indices, counters. This can be taken * within the scheduler's rq lock. It must be released before calling * console_unlock() or anything else that might wake up a process. */ DEFINE_RAW_SPINLOCK(logbuf_lock); /* * Helper macros to lock/unlock logbuf_lock and switch between * printk-safe/unsafe modes. */ #define logbuf_lock_irq() \ do { \ printk_safe_enter_irq(); \ raw_spin_lock(&logbuf_lock); \ } while (0) #define logbuf_unlock_irq() \ do { \ raw_spin_unlock(&logbuf_lock); \ printk_safe_exit_irq(); \ } while (0) #define logbuf_lock_irqsave(flags) \ do { \ printk_safe_enter_irqsave(flags); \ raw_spin_lock(&logbuf_lock); \ } while (0) #define logbuf_unlock_irqrestore(flags) \ do { \ raw_spin_unlock(&logbuf_lock); \ printk_safe_exit_irqrestore(flags); \ } while (0) #ifdef CONFIG_PRINTK DECLARE_WAIT_QUEUE_HEAD(log_wait); /* the next printk record to read by syslog(READ) or /proc/kmsg */ static u64 syslog_seq; static u32 syslog_idx; static size_t syslog_partial; /* index and sequence number of the first record stored in the buffer */ static u64 log_first_seq; static u32 log_first_idx; /* index and sequence number of the next record to store in the buffer */ static u64 log_next_seq; static u32 log_next_idx; /* the next printk record to write to the console */ static u64 console_seq; static u32 console_idx; /* the next printk record to read after the last 'clear' command */ static u64 clear_seq; static u32 clear_idx; #define PREFIX_MAX 32 #define LOG_LINE_MAX (1024 - PREFIX_MAX) #define LOG_LEVEL(v) ((v) & 0x07) #define LOG_FACILITY(v) ((v) >> 3 & 0xff) /* record buffer */ #define LOG_ALIGN __alignof__(struct printk_log) #define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT) static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN); static char *log_buf = __log_buf; static u32 log_buf_len = __LOG_BUF_LEN; /* Return log buffer address */
char *log_buf_addr_get(void) { return log_buf; }

Contributors

PersonTokensPropCommitsCommitProp
Vasant Hegde11100.00%1100.00%
Total11100.00%1100.00%

/* Return log buffer size */
u32 log_buf_len_get(void) { return log_buf_len; }

Contributors

PersonTokensPropCommitsCommitProp
Vasant Hegde10100.00%1100.00%
Total10100.00%1100.00%

/* human readable text of the record */
static char *log_text(const struct printk_log *msg) { return (char *)msg + sizeof(struct printk_log); }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers1557.69%120.00%
Andrew Morton519.23%120.00%
Mike Travis27.69%120.00%
Werner Almesberger27.69%120.00%
Joe Perches27.69%120.00%
Total26100.00%5100.00%

/* optional key/value pair dictionary attached to the record */
static char *log_dict(const struct printk_log *msg) { return (char *)msg + sizeof(struct printk_log) + msg->text_len; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers2480.00%125.00%
Joe Perches26.67%125.00%
Mike Travis26.67%125.00%
Andrew Morton26.67%125.00%
Total30100.00%4100.00%

/* get record by index; idx must point to valid msg */
static struct printk_log *log_from_idx(u32 idx) { struct printk_log *msg = (struct printk_log *)(log_buf + idx); /* * A length == 0 record is the end of buffer marker. Wrap around and * read the message at the start of the buffer. */ if (!msg->len) return (struct printk_log *)log_buf; return msg; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers3678.26%125.00%
Mike Travis510.87%125.00%
Joe Perches48.70%125.00%
Andrew Morton12.17%125.00%
Total46100.00%4100.00%

/* get next record; idx must point to valid msg */
static u32 log_next(u32 idx) { struct printk_log *msg = (struct printk_log *)(log_buf + idx); /* length == 0 indicates the end of the buffer; wrap */ /* * A length == 0 record is the end of buffer marker. Wrap around and * read the message at the start of the buffer as *this* one, and * return the one after that. */ if (!msg->len) { msg = (struct printk_log *)log_buf; return msg->len; } return idx + msg->len; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers5087.72%133.33%
Mike Travis47.02%133.33%
Joe Perches35.26%133.33%
Total57100.00%3100.00%

/* * Check whether there is enough free space for the given message. * * The same values of first_idx and next_idx mean that the buffer * is either empty or full. * * If the buffer is empty, we must respect the position of the indexes. * They cannot be reset to the beginning of the buffer. */
static int logbuf_has_space(u32 msg_size, bool empty) { u32 free; if (log_next_idx > log_first_idx || empty) free = max(log_buf_len - log_next_idx, log_first_idx); else free = log_first_idx - log_next_idx; /* * We need space also for an empty header that signalizes wrapping * of the buffer. */ return free >= msg_size + sizeof(struct printk_log); }

Contributors

PersonTokensPropCommitsCommitProp
Petr Mladek53100.00%2100.00%
Total53100.00%2100.00%


static int log_make_free_space(u32 msg_size) { while (log_first_seq < log_next_seq && !logbuf_has_space(msg_size, false)) { /* drop old messages until we have enough contiguous space */ log_first_idx = log_next(log_first_idx); log_first_seq++; } if (clear_seq < log_first_seq) { clear_seq = log_first_seq; clear_idx = log_first_idx; } /* sequence numbers are equal, so the log buffer is empty */ if (logbuf_has_space(msg_size, log_first_seq == log_next_seq)) return 0; return -ENOMEM; }

Contributors

PersonTokensPropCommitsCommitProp
Petr Mladek4867.61%250.00%
Ivan Delalande2230.99%125.00%
Alex Elder11.41%125.00%
Total71100.00%4100.00%

/* compute the message size including the padding bytes */
static u32 msg_used_size(u16 text_len, u16 dict_len, u32 *pad_len) { u32 size; size = sizeof(struct printk_log) + text_len + dict_len; *pad_len = (-size) & (LOG_ALIGN - 1); size += *pad_len; return size; }

Contributors

PersonTokensPropCommitsCommitProp
Petr Mladek53100.00%1100.00%
Total53100.00%1100.00%

/* * Define how much of the log buffer we could take at maximum. The value * must be greater than two. Note that only half of the buffer is available * when the index points to the middle. */ #define MAX_LOG_TAKE_PART 4 static const char trunc_msg[] = "<truncated>";
static u32 truncate_msg(u16 *text_len, u16 *trunc_msg_len, u16 *dict_len, u32 *pad_len) { /* * The message should not take the whole buffer. Otherwise, it might * get removed too soon. */ u32 max_text_len = log_buf_len / MAX_LOG_TAKE_PART; if (*text_len > max_text_len) *text_len = max_text_len; /* enable the warning message */ *trunc_msg_len = strlen(trunc_msg); /* disable the "dict" completely */ *dict_len = 0; /* compute the size again, count also the warning message */ return msg_used_size(*text_len + *trunc_msg_len, 0, pad_len); }

Contributors

PersonTokensPropCommitsCommitProp
Petr Mladek72100.00%1100.00%
Total72100.00%1100.00%

/* insert record into the buffer, discard old ones, update heads */
static int log_store(int facility, int level, enum log_flags flags, u64 ts_nsec, const char *dict, u16 dict_len, const char *text, u16 text_len) { struct printk_log *msg; u32 size, pad_len; u16 trunc_msg_len = 0; /* number of '\0' padding bytes to next message */ size = msg_used_size(text_len, dict_len, &pad_len); if (log_make_free_space(size)) { /* truncate the message if it is too long for empty buffer */ size = truncate_msg(&text_len, &trunc_msg_len, &dict_len, &pad_len); /* survive when the log buffer is too small for trunc_msg */ if (log_make_free_space(size)) return 0; } if (log_next_idx + size + sizeof(struct printk_log) > log_buf_len) { /* * This message + an additional empty header does not fit * at the end of the buffer. Add an empty header with len == 0 * to signify a wrap around. */ memset(log_buf + log_next_idx, 0, sizeof(struct printk_log)); log_next_idx = 0; } /* fill message */ msg = (struct printk_log *)(log_buf + log_next_idx); memcpy(log_text(msg), text, text_len); msg->text_len = text_len; if (trunc_msg_len) { memcpy(log_text(msg) + text_len, trunc_msg, trunc_msg_len); msg->text_len += trunc_msg_len; } memcpy(log_dict(msg), dict, dict_len); msg->dict_len = dict_len; msg->facility = facility; msg->level = level & 7; msg->flags = flags & 0x1f; if (ts_nsec > 0) msg->ts_nsec = ts_nsec; else msg->ts_nsec = local_clock(); memset(log_dict(msg) + dict_len, 0, pad_len); msg->len = size; /* insert message */ log_next_idx += msg->len; log_next_seq++; return msg->text_len; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers17961.72%216.67%
Petr Mladek7826.90%758.33%
Andrew Morton206.90%18.33%
Mike Travis93.10%18.33%
Joe Perches41.38%18.33%
Total290100.00%12100.00%

int dmesg_restrict = IS_ENABLED(CONFIG_SECURITY_DMESG_RESTRICT);
static int syslog_action_restricted(int type) { if (dmesg_restrict) return 1; /* * Unless restricted, we allow "read all" and "get buffer size" * for everybody. */ return type != SYSLOG_ACTION_READ_ALL && type != SYSLOG_ACTION_SIZE_BUFFER; }

Contributors

PersonTokensPropCommitsCommitProp
Kees Cook26100.00%1100.00%
Total26100.00%1100.00%


static int check_syslog_permissions(int type, int source) { /* * If this is from /proc/kmsg and we've already opened it, then we've * already done the capabilities checks at open time. */ if (source == SYSLOG_FROM_PROC && type != SYSLOG_ACTION_OPEN) goto ok; if (syslog_action_restricted(type)) { if (capable(CAP_SYSLOG)) goto ok; /* * For historical reasons, accept CAP_SYS_ADMIN too, with * a warning. */ if (capable(CAP_SYS_ADMIN)) { pr_warn_once("%s (%d): Attempt to access syslog with " "CAP_SYS_ADMIN but no CAP_SYSLOG " "(deprecated).\n", current->comm, task_pid_nr(current)); goto ok; } return -EPERM; } ok: return security_syslog(type); }

Contributors

PersonTokensPropCommitsCommitProp
Kees Cook7081.40%250.00%
Vasily Averin1618.60%250.00%
Total86100.00%4100.00%


static void append_char(char **pp, char *e, char c) { if (*pp < e) *(*pp)++ = c; }

Contributors

PersonTokensPropCommitsCommitProp
Tejun Heo34100.00%1100.00%
Total34100.00%1100.00%


static ssize_t msg_print_ext_header(char *buf, size_t size, struct printk_log *msg, u64 seq) { u64 ts_usec = msg->ts_nsec; do_div(ts_usec, 1000); return scnprintf(buf, size, "%u,%llu,%llu,%c;", (msg->facility << 3) | msg->level, seq, ts_usec, msg->flags & LOG_CONT ? 'c' : '-'); }

Contributors

PersonTokensPropCommitsCommitProp
Tejun Heo4259.15%120.00%
Kay Sievers1521.13%120.00%
Linus Torvalds912.68%120.00%
Al Viro34.23%120.00%
Christoph Hellwig22.82%120.00%
Total71100.00%5100.00%


static ssize_t msg_print_ext_body(char *buf, size_t size, char *dict, size_t dict_len, char *text, size_t text_len) { char *p = buf, *e = buf + size; size_t i; /* escape non-printable characters */ for (i = 0; i < text_len; i++) { unsigned char c = text[i]; if (c < ' ' || c >= 127 || c == '\\') p += scnprintf(p, e - p, "\\x%02x", c); else append_char(&p, e, c); } append_char(&p, e, '\n'); if (dict_len) { bool line = true; for (i = 0; i < dict_len; i++) { unsigned char c = dict[i]; if (line) { append_char(&p, e, ' '); line = false; } if (c == '\0') { append_char(&p, e, '\n'); line = true; continue; } if (c < ' ' || c >= 127 || c == '\\') { p += scnprintf(p, e - p, "\\x%02x", c); continue; } append_char(&p, e, c); } append_char(&p, e, '\n'); } return p - buf; }

Contributors

PersonTokensPropCommitsCommitProp
Tejun Heo20982.61%150.00%
Kay Sievers4417.39%150.00%
Total253100.00%2100.00%

/* /dev/kmsg - userspace message inject/listen interface */ struct devkmsg_user { u64 seq; u32 idx; struct ratelimit_state rs; struct mutex lock; char buf[CONSOLE_EXT_LOG_MAX]; };
static ssize_t devkmsg_write(struct kiocb *iocb, struct iov_iter *from) { char *buf, *line; int level = default_message_loglevel; int facility = 1; /* LOG_USER */ struct file *file = iocb->ki_filp; struct devkmsg_user *user = file->private_data; size_t len = iov_iter_count(from); ssize_t ret = len; if (!user || len > LOG_LINE_MAX) return -EINVAL; /* Ignore when user logging is disabled. */ if (devkmsg_log & DEVKMSG_LOG_MASK_OFF) return len; /* Ratelimit when not explicitly enabled. */ if (!(devkmsg_log & DEVKMSG_LOG_MASK_ON)) { if (!___ratelimit(&user->rs, current->comm)) return ret; } buf = kmalloc(len+1, GFP_KERNEL); if (buf == NULL) return -ENOMEM; buf[len] = '\0'; if (!copy_from_iter_full(buf, len, from)) { kfree(buf); return -EFAULT; } /* * Extract and skip the syslog prefix <[0-9]*>. Coming from userspace * the decimal value represents 32bit, the lower 3 bit are the log * level, the rest are the log facility. * * If no prefix or no userspace facility is specified, we * enforce LOG_USER, to be able to reliably distinguish * kernel-generated messages from userspace-injected ones. */ line = buf; if (line[0] == '<') { char *endp = NULL; unsigned int u; u = simple_strtoul(line + 1, &endp, 10); if (endp && endp[0] == '>') { level = LOG_LEVEL(u); if (LOG_FACILITY(u) != 0) facility = LOG_FACILITY(u); endp++; len -= endp - line; line = endp; } } printk_emit(facility, level, NULL, 0, "%s", line); kfree(buf); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Tejun Heo13949.47%222.22%
Borislav Petkov6121.71%111.11%
Kay Sievers5419.22%333.33%
Mathias Krause196.76%111.11%
Yuanhan Liu62.14%111.11%
Al Viro20.71%111.11%
Total281100.00%9100.00%


static ssize_t devkmsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct devkmsg_user *user = file->private_data; struct printk_log *msg; size_t len; ssize_t ret; if (!user) return -EBADF; ret = mutex_lock_interruptible(&user->lock); if (ret) return ret; logbuf_lock_irq(); while (user->seq == log_next_seq) { if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; logbuf_unlock_irq(); goto out; } logbuf_unlock_irq(); ret = wait_event_interruptible(log_wait, user->seq != log_next_seq); if (ret) goto out; logbuf_lock_irq(); } if (user->seq < log_first_seq) { /* our last seen message is gone, return error and reset */ user->idx = log_first_idx; user->seq = log_first_seq; ret = -EPIPE; logbuf_unlock_irq(); goto out; } msg = log_from_idx(user->idx); len = msg_print_ext_header(user->buf, sizeof(user->buf), msg, user->seq); len += msg_print_ext_body(user->buf + len, sizeof