1111#include "include/kasld/sysroot.h"
1212
1313#include <errno.h>
14+ #include <limits.h>
1415#include <stddef.h>
1516#include <stdio.h>
1617#include <sys/klog.h>
2021#define SYSLOG_ACTION_READ_ALL 3
2122#define SYSLOG_ACTION_SIZE_BUFFER 10
2223
24+ /* Size the mapping for a `len`-byte kernel log: `len` page-rounded STRICTLY
25+ * upward, so every byte of a len-sized read lands inside the mapping and
26+ * buffer[len] is still a writable byte the caller may terminate on. Callers
27+ * walking the buffer by line depend on that byte existing.
28+ *
29+ * Returns 0 when `len` is out of range, which both sources treat as "no log to
30+ * read". The bound is that the rounded size must still fit a positive int:
31+ * that is the width this interface reports a size in, and `int` is also what
32+ * klogctl() takes and returns. A log larger than that is refused rather than
33+ * partly read — a prefix of a kernel log would silently drop whatever leaks sat
34+ * in the rest of it.
35+ *
36+ * Both sources round through here so that neither can size a mapping from one
37+ * value and then fill it from another. */
38+ static size_t kasld_syslog_alloc (long len ) {
39+ long page = getpagesize ();
40+
41+ if (len <= 0 || page <= 0 || len > (long )INT_MAX - page )
42+ return 0 ;
43+ return (size_t )((len / page + 1 ) * page );
44+ }
45+
2346/* Read /var/log/dmesg into an mmap'd buffer.
2447 * Fallback when klogctl() is denied (dmesg_restrict=1).
2548 */
2649static int read_dmesg_log_file (char * * buffer , int * size ) {
2750 FILE * f ;
2851 long len ;
29- int alloc ;
52+ size_t alloc ;
3053 const char * path = "/var/log/dmesg" ;
3154
3255 f = kasld_fopen (path , "rb" );
@@ -42,15 +65,23 @@ static int read_dmesg_log_file(char **buffer, int *size) {
4265
4366 rewind (f );
4467
45- alloc = ((int )len / getpagesize () + 1 ) * getpagesize ();
68+ /* The mapping and the read are sized from the same `len`. Under
69+ * KASLD_SYSROOT this file comes from a captured tree rather than from the
70+ * running kernel, so its length is whatever that tree says it is. */
71+ alloc = kasld_syslog_alloc (len );
72+ if (alloc == 0 ) {
73+ fprintf (stderr , "[-] %s: implausible length (%ld bytes)\n" , path , len );
74+ fclose (f );
75+ return 1 ;
76+ }
4677 * buffer = (char * )mmap (NULL , alloc , PROT_READ | PROT_WRITE ,
4778 MAP_PRIVATE | MAP_ANONYMOUS , -1 , 0 );
4879 if (* buffer == MAP_FAILED ) {
4980 fclose (f );
5081 return 1 ;
5182 }
5283
53- * size = (int )fread (* buffer , 1 , len , f );
84+ * size = (int )fread (* buffer , 1 , ( size_t ) len , f );
5485 fclose (f );
5586
5687 if (* size <= 0 ) {
@@ -64,14 +95,20 @@ static int read_dmesg_log_file(char **buffer, int *size) {
6495/* mmap entire kernel message ring buffer into +buffer+.
6596 * Falls back to /var/log/dmesg when klogctl() is denied.
6697 *
98+ * On success *size is the number of bytes read and the mapping is strictly
99+ * larger, so buffer[*size] is a writable byte inside it: a caller may walk the
100+ * buffer by line and terminate the last one in place. Both sources establish
101+ * that through kasld_syslog_alloc().
102+ *
67103 * Copied from exploit code by xairy:
68104 * https://github.com/xairy/kernel-exploits/blob/master/CVE-2017-1000112/poc.c
69105 *
70106 * Defined static: each component is compiled as a standalone binary,
71107 * so header-embedded implementations are the intended pattern.
72108 */
73109static int mmap_syslog (char * * buffer , int * size ) {
74- int alloc ;
110+ size_t alloc ;
111+ int ring ;
75112
76113 /* Offline analysis: under KASLD_SYSROOT, klogctl() would read the live HOST
77114 * kernel log, not the analysed tree, so read the captured /var/log/dmesg
@@ -81,26 +118,40 @@ static int mmap_syslog(char **buffer, int *size) {
81118 if (kasld_sysroot ())
82119 return read_dmesg_log_file (buffer , size );
83120
84- * size = klogctl (SYSLOG_ACTION_SIZE_BUFFER , 0 , 0 );
85- if (* size == -1 ) {
121+ /* The reported ring size is held apart from *size until the read succeeds,
122+ * so a fallback to the log file never inherits a size from the ring. */
123+ ring = klogctl (SYSLOG_ACTION_SIZE_BUFFER , 0 , 0 );
124+ if (ring < 0 ) {
86125 perror ("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)" );
87126 return read_dmesg_log_file (buffer , size );
88127 }
89128
90- alloc = (* size / getpagesize () + 1 ) * getpagesize ();
129+ /* A ring can be raised to 2 GiB by the log_buf_len boot parameter, which is
130+ * past what the rounded size can report as a positive int. */
131+ alloc = kasld_syslog_alloc (ring );
132+ if (alloc == 0 )
133+ return read_dmesg_log_file (buffer , size );
134+
91135 * buffer = (char * )mmap (NULL , alloc , PROT_READ | PROT_WRITE ,
92136 MAP_PRIVATE | MAP_ANONYMOUS , -1 , 0 );
93137 if (* buffer == MAP_FAILED ) {
94138 return read_dmesg_log_file (buffer , size );
95139 }
96140
97- * size = klogctl (SYSLOG_ACTION_READ_ALL , * buffer , alloc );
98- if (* size == -1 ) {
141+ * size = klogctl (SYSLOG_ACTION_READ_ALL , * buffer , ( int ) alloc );
142+ if (* size < 0 ) {
99143 perror ("[-] klogctl(SYSLOG_ACTION_READ_ALL)" );
100144 munmap (* buffer , alloc );
101145 return read_dmesg_log_file (buffer , size );
102146 }
103147
148+ /* alloc is strictly above the size the kernel reported, so a full read still
149+ * leaves buffer[*size] inside the mapping. Clamped rather than assumed: the
150+ * terminable-byte invariant must not rest on the two klogctl calls agreeing
151+ * about how much the ring holds. */
152+ if ((size_t )* size >= alloc )
153+ * size = (int )alloc - 1 ;
154+
104155 return 0 ;
105156}
106157
0 commit comments