diff options
-rw-r--r-- | Documentation/kernel-parameters.txt | 7 | ||||
-rw-r--r-- | Documentation/sysctl/kernel.txt | 14 | ||||
-rw-r--r-- | include/linux/printk.h | 9 | ||||
-rw-r--r-- | kernel/printk/printk.c | 142 | ||||
-rw-r--r-- | kernel/sysctl.c | 7 |
5 files changed, 171 insertions, 8 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index e24aa11e8f8a..b240540e49f2 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
@@ -3173,6 +3173,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted. | |||
3173 | Format: <bool> (1/Y/y=enable, 0/N/n=disable) | 3173 | Format: <bool> (1/Y/y=enable, 0/N/n=disable) |
3174 | default: disabled | 3174 | default: disabled |
3175 | 3175 | ||
3176 | printk.devkmsg={on,off,ratelimit} | ||
3177 | Control writing to /dev/kmsg. | ||
3178 | on - unlimited logging to /dev/kmsg from userspace | ||
3179 | off - logging to /dev/kmsg disabled | ||
3180 | ratelimit - ratelimit the logging | ||
3181 | Default: ratelimit | ||
3182 | |||
3176 | printk.time= Show timing data prefixed to each printk message line | 3183 | printk.time= Show timing data prefixed to each printk message line |
3177 | Format: <bool> (1/Y/y=enable, 0/N/n=disable) | 3184 | Format: <bool> (1/Y/y=enable, 0/N/n=disable) |
3178 | 3185 | ||
diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 33204604de6c..ffab8b5caa60 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt | |||
@@ -764,6 +764,20 @@ send before ratelimiting kicks in. | |||
764 | 764 | ||
765 | ============================================================== | 765 | ============================================================== |
766 | 766 | ||
767 | printk_devkmsg: | ||
768 | |||
769 | Control the logging to /dev/kmsg from userspace: | ||
770 | |||
771 | ratelimit: default, ratelimited | ||
772 | on: unlimited logging to /dev/kmsg from userspace | ||
773 | off: logging to /dev/kmsg disabled | ||
774 | |||
775 | The kernel command line parameter printk.devkmsg= overrides this and is | ||
776 | a one-time setting until next reboot: once set, it cannot be changed by | ||
777 | this sysctl interface anymore. | ||
778 | |||
779 | ============================================================== | ||
780 | |||
767 | randomize_va_space: | 781 | randomize_va_space: |
768 | 782 | ||
769 | This option can be used to select the type of process address | 783 | This option can be used to select the type of process address |
diff --git a/include/linux/printk.h b/include/linux/printk.h index c2158f0f1499..8dc155dab3ed 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h | |||
@@ -61,6 +61,11 @@ static inline void console_verbose(void) | |||
61 | console_loglevel = CONSOLE_LOGLEVEL_MOTORMOUTH; | 61 | console_loglevel = CONSOLE_LOGLEVEL_MOTORMOUTH; |
62 | } | 62 | } |
63 | 63 | ||
64 | /* strlen("ratelimit") + 1 */ | ||
65 | #define DEVKMSG_STR_MAX_SIZE 10 | ||
66 | extern char devkmsg_log_str[]; | ||
67 | struct ctl_table; | ||
68 | |||
64 | struct va_format { | 69 | struct va_format { |
65 | const char *fmt; | 70 | const char *fmt; |
66 | va_list *va; | 71 | va_list *va; |
@@ -175,6 +180,10 @@ extern int printk_delay_msec; | |||
175 | extern int dmesg_restrict; | 180 | extern int dmesg_restrict; |
176 | extern int kptr_restrict; | 181 | extern int kptr_restrict; |
177 | 182 | ||
183 | extern int | ||
184 | devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write, void __user *buf, | ||
185 | size_t *lenp, loff_t *ppos); | ||
186 | |||
178 | extern void wake_up_klogd(void); | 187 | extern void wake_up_klogd(void); |
179 | 188 | ||
180 | char *log_buf_addr_get(void); | 189 | char *log_buf_addr_get(void); |
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 70c66c5ba212..a5ef95ca18c9 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c | |||
@@ -85,6 +85,111 @@ static struct lockdep_map console_lock_dep_map = { | |||
85 | }; | 85 | }; |
86 | #endif | 86 | #endif |
87 | 87 | ||
88 | enum devkmsg_log_bits { | ||
89 | __DEVKMSG_LOG_BIT_ON = 0, | ||
90 | __DEVKMSG_LOG_BIT_OFF, | ||
91 | __DEVKMSG_LOG_BIT_LOCK, | ||
92 | }; | ||
93 | |||
94 | enum devkmsg_log_masks { | ||
95 | DEVKMSG_LOG_MASK_ON = BIT(__DEVKMSG_LOG_BIT_ON), | ||
96 | DEVKMSG_LOG_MASK_OFF = BIT(__DEVKMSG_LOG_BIT_OFF), | ||
97 | DEVKMSG_LOG_MASK_LOCK = BIT(__DEVKMSG_LOG_BIT_LOCK), | ||
98 | }; | ||
99 | |||
100 | /* Keep both the 'on' and 'off' bits clear, i.e. ratelimit by default: */ | ||
101 | #define DEVKMSG_LOG_MASK_DEFAULT 0 | ||
102 | |||
103 | static unsigned int __read_mostly devkmsg_log = DEVKMSG_LOG_MASK_DEFAULT; | ||
104 | |||
105 | static int __control_devkmsg(char *str) | ||
106 | { | ||
107 | if (!str) | ||
108 | return -EINVAL; | ||
109 | |||
110 | if (!strncmp(str, "on", 2)) { | ||
111 | devkmsg_log = DEVKMSG_LOG_MASK_ON; | ||
112 | return 2; | ||
113 | } else if (!strncmp(str, "off", 3)) { | ||
114 | devkmsg_log = DEVKMSG_LOG_MASK_OFF; | ||
115 | return 3; | ||
116 | } else if (!strncmp(str, "ratelimit", 9)) { | ||
117 | devkmsg_log = DEVKMSG_LOG_MASK_DEFAULT; | ||
118 | return 9; | ||
119 | } | ||
120 | return -EINVAL; | ||
121 | } | ||
122 | |||
123 | static int __init control_devkmsg(char *str) | ||
124 | { | ||
125 | if (__control_devkmsg(str) < 0) | ||
126 | return 1; | ||
127 | |||
128 | /* | ||
129 | * Set sysctl string accordingly: | ||
130 | */ | ||
131 | if (devkmsg_log == DEVKMSG_LOG_MASK_ON) { | ||
132 | memset(devkmsg_log_str, 0, DEVKMSG_STR_MAX_SIZE); | ||
133 | strncpy(devkmsg_log_str, "on", 2); | ||
134 | } else if (devkmsg_log == DEVKMSG_LOG_MASK_OFF) { | ||
135 | memset(devkmsg_log_str, 0, DEVKMSG_STR_MAX_SIZE); | ||
136 | strncpy(devkmsg_log_str, "off", 3); | ||
137 | } | ||
138 | /* else "ratelimit" which is set by default. */ | ||
139 | |||
140 | /* | ||
141 | * Sysctl cannot change it anymore. The kernel command line setting of | ||
142 | * this parameter is to force the setting to be permanent throughout the | ||
143 | * runtime of the system. This is a precation measure against userspace | ||
144 | * trying to be a smarta** and attempting to change it up on us. | ||
145 | */ | ||
146 | devkmsg_log |= DEVKMSG_LOG_MASK_LOCK; | ||
147 | |||
148 | return 0; | ||
149 | } | ||
150 | __setup("printk.devkmsg=", control_devkmsg); | ||
151 | |||
152 | char devkmsg_log_str[DEVKMSG_STR_MAX_SIZE] = "ratelimit"; | ||
153 | |||
154 | int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write, | ||
155 | void __user *buffer, size_t *lenp, loff_t *ppos) | ||
156 | { | ||
157 | char old_str[DEVKMSG_STR_MAX_SIZE]; | ||
158 | unsigned int old; | ||
159 | int err; | ||
160 | |||
161 | if (write) { | ||
162 | if (devkmsg_log & DEVKMSG_LOG_MASK_LOCK) | ||
163 | return -EINVAL; | ||
164 | |||
165 | old = devkmsg_log; | ||
166 | strncpy(old_str, devkmsg_log_str, DEVKMSG_STR_MAX_SIZE); | ||
167 | } | ||
168 | |||
169 | err = proc_dostring(table, write, buffer, lenp, ppos); | ||
170 | if (err) | ||
171 | return err; | ||
172 | |||
173 | if (write) { | ||
174 | err = __control_devkmsg(devkmsg_log_str); | ||
175 | |||
176 | /* | ||
177 | * Do not accept an unknown string OR a known string with | ||
178 | * trailing crap... | ||
179 | */ | ||
180 | if (err < 0 || (err + 1 != *lenp)) { | ||
181 | |||
182 | /* ... and restore old setting. */ | ||
183 | devkmsg_log = old; | ||
184 | strncpy(devkmsg_log_str, old_str, DEVKMSG_STR_MAX_SIZE); | ||
185 | |||
186 | return -EINVAL; | ||
187 | } | ||
188 | } | ||
189 | |||
190 | return 0; | ||
191 | } | ||
192 | |||
88 | /* | 193 | /* |
89 | * Number of registered extended console drivers. | 194 | * Number of registered extended console drivers. |
90 | * | 195 | * |
@@ -613,6 +718,7 @@ struct devkmsg_user { | |||
613 | u64 seq; | 718 | u64 seq; |
614 | u32 idx; | 719 | u32 idx; |
615 | enum log_flags prev; | 720 | enum log_flags prev; |
721 | struct ratelimit_state rs; | ||
616 | struct mutex lock; | 722 | struct mutex lock; |
617 | char buf[CONSOLE_EXT_LOG_MAX]; | 723 | char buf[CONSOLE_EXT_LOG_MAX]; |
618 | }; | 724 | }; |
@@ -622,11 +728,24 @@ static ssize_t devkmsg_write(struct kiocb *iocb, struct iov_iter *from) | |||
622 | char *buf, *line; | 728 | char *buf, *line; |
623 | int level = default_message_loglevel; | 729 | int level = default_message_loglevel; |
624 | int facility = 1; /* LOG_USER */ | 730 | int facility = 1; /* LOG_USER */ |
731 | struct file *file = iocb->ki_filp; | ||
732 | struct devkmsg_user *user = file->private_data; | ||
625 | size_t len = iov_iter_count(from); | 733 | size_t len = iov_iter_count(from); |
626 | ssize_t ret = len; | 734 | ssize_t ret = len; |
627 | 735 | ||
628 | if (len > LOG_LINE_MAX) | 736 | if (!user || len > LOG_LINE_MAX) |
629 | return -EINVAL; | 737 | return -EINVAL; |
738 | |||
739 | /* Ignore when user logging is disabled. */ | ||
740 | if (devkmsg_log & DEVKMSG_LOG_MASK_OFF) | ||
741 | return len; | ||
742 | |||
743 | /* Ratelimit when not explicitly enabled. */ | ||
744 | if (!(devkmsg_log & DEVKMSG_LOG_MASK_ON)) { | ||
745 | if (!___ratelimit(&user->rs, current->comm)) | ||
746 | return ret; | ||
747 | } | ||
748 | |||
630 | buf = kmalloc(len+1, GFP_KERNEL); | 749 | buf = kmalloc(len+1, GFP_KERNEL); |
631 | if (buf == NULL) | 750 | if (buf == NULL) |
632 | return -ENOMEM; | 751 | return -ENOMEM; |
@@ -799,19 +918,24 @@ static int devkmsg_open(struct inode *inode, struct file *file) | |||
799 | struct devkmsg_user *user; | 918 | struct devkmsg_user *user; |
800 | int err; | 919 | int err; |
801 | 920 | ||
802 | /* write-only does not need any file context */ | 921 | if (devkmsg_log & DEVKMSG_LOG_MASK_OFF) |
803 | if ((file->f_flags & O_ACCMODE) == O_WRONLY) | 922 | return -EPERM; |
804 | return 0; | ||
805 | 923 | ||
806 | err = check_syslog_permissions(SYSLOG_ACTION_READ_ALL, | 924 | /* write-only does not need any file context */ |
807 | SYSLOG_FROM_READER); | 925 | if ((file->f_flags & O_ACCMODE) != O_WRONLY) { |
808 | if (err) | 926 | err = check_syslog_permissions(SYSLOG_ACTION_READ_ALL, |
809 | return err; | 927 | SYSLOG_FROM_READER); |
928 | if (err) | ||
929 | return err; | ||
930 | } | ||
810 | 931 | ||
811 | user = kmalloc(sizeof(struct devkmsg_user), GFP_KERNEL); | 932 | user = kmalloc(sizeof(struct devkmsg_user), GFP_KERNEL); |
812 | if (!user) | 933 | if (!user) |
813 | return -ENOMEM; | 934 | return -ENOMEM; |
814 | 935 | ||
936 | ratelimit_default_init(&user->rs); | ||
937 | ratelimit_set_flags(&user->rs, RATELIMIT_MSG_ON_RELEASE); | ||
938 | |||
815 | mutex_init(&user->lock); | 939 | mutex_init(&user->lock); |
816 | 940 | ||
817 | raw_spin_lock_irq(&logbuf_lock); | 941 | raw_spin_lock_irq(&logbuf_lock); |
@@ -830,6 +954,8 @@ static int devkmsg_release(struct inode *inode, struct file *file) | |||
830 | if (!user) | 954 | if (!user) |
831 | return 0; | 955 | return 0; |
832 | 956 | ||
957 | ratelimit_state_exit(&user->rs); | ||
958 | |||
833 | mutex_destroy(&user->lock); | 959 | mutex_destroy(&user->lock); |
834 | kfree(user); | 960 | kfree(user); |
835 | return 0; | 961 | return 0; |
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 53954631a4e1..b43d0b27c1fe 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -814,6 +814,13 @@ static struct ctl_table kern_table[] = { | |||
814 | .extra2 = &ten_thousand, | 814 | .extra2 = &ten_thousand, |
815 | }, | 815 | }, |
816 | { | 816 | { |
817 | .procname = "printk_devkmsg", | ||
818 | .data = devkmsg_log_str, | ||
819 | .maxlen = DEVKMSG_STR_MAX_SIZE, | ||
820 | .mode = 0644, | ||
821 | .proc_handler = devkmsg_sysctl_set_loglvl, | ||
822 | }, | ||
823 | { | ||
817 | .procname = "dmesg_restrict", | 824 | .procname = "dmesg_restrict", |
818 | .data = &dmesg_restrict, | 825 | .data = &dmesg_restrict, |
819 | .maxlen = sizeof(int), | 826 | .maxlen = sizeof(int), |