1    	/*
2    	 * Copyright (C) 2011 Red Hat, Inc.
3    	 *
4    	 * All rights reserved.
5    	 *
6    	 * Author: Angus Salkeld <asalkeld@redhat.com>
7    	 *
8    	 * libqb is free software: you can redistribute it and/or modify
9    	 * it under the terms of the GNU Lesser General Public License as published by
10   	 * the Free Software Foundation, either version 2.1 of the License, or
11   	 * (at your option) any later version.
12   	 *
13   	 * libqb is distributed in the hope that it will be useful,
14   	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   	 * GNU Lesser General Public License for more details.
17   	 *
18   	 * You should have received a copy of the GNU Lesser General Public License
19   	 * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
20   	 */
21   	#include "os_base.h"
22   	
23   	#include <qb/qbrb.h>
24   	#include "util_int.h"
25   	#include "log_int.h"
26   	#include "ringbuffer_int.h"
27   	
28   	#define BB_MIN_ENTRY_SIZE (4 * sizeof(uint32_t) +\
29   				   sizeof(uint8_t) +\
30   				   2 * sizeof(char) + sizeof(time_t))
31   	
32   	
33   	static void
34   	_blackbox_reload(int32_t target)
35   	{
36   		struct qb_log_target *t = qb_log_target_get(target);
37   	
38   		if (t->instance == NULL) {
39   			return;
40   		}
41   		qb_rb_close(t->instance);
42   		t->instance = qb_rb_open(t->filename, t->size,
43   					 QB_RB_FLAG_CREATE | QB_RB_FLAG_OVERWRITE, 0);
44   	}
45   	
46   	/* <u32> file lineno
47   	 * <u32> tags
48   	 * <u8> priority
49   	 * <u32> function name length
50   	 * <string> function name
51   	 * <u32> buffer length
52   	 * <string> buffer
53   	 */
54   	static void
55   	_blackbox_vlogger(int32_t target,
56   			  struct qb_log_callsite *cs, struct timespec *timestamp, va_list ap)
57   	{
58   		size_t max_size;
59   		size_t actual_size;
60   		uint32_t fn_size;
61   		char *chunk;
62   		char *msg_len_pt;
63   		uint32_t msg_len;
64   		struct qb_log_target *t = qb_log_target_get(target);
65   	
66   		if (t->instance == NULL) {
67   			return;
68   		}
69   	
70   		fn_size = strlen(cs->function) + 1;
71   	
72   		actual_size = 4 * sizeof(uint32_t) + sizeof(uint8_t) + fn_size + sizeof(struct timespec);
73   		max_size = actual_size + t->max_line_length;
74   	
75   		chunk = qb_rb_chunk_alloc(t->instance, max_size);
76   	
77   		if (chunk == NULL) {
78   			/* something bad has happened. abort blackbox logging */
79   			qb_util_perror(LOG_ERR, "Blackbox allocation error, aborting blackbox log %s", t->filename);
80   			qb_rb_close(qb_rb_lastref_and_ret(
81   				(struct qb_ringbuffer_s **) &t->instance
82   			));
83   			return;
84   		}
85   	
86   		/* line number */
87   		memcpy(chunk, &cs->lineno, sizeof(uint32_t));
88   		chunk += sizeof(uint32_t);
89   	
90   		/* tags */
91   		memcpy(chunk, &cs->tags, sizeof(uint32_t));
92   		chunk += sizeof(uint32_t);
93   	
94   		/* log level/priority */
95   		memcpy(chunk, &cs->priority, sizeof(uint8_t));
96   		chunk += sizeof(uint8_t);
97   	
98   		/* function name */
99   		memcpy(chunk, &fn_size, sizeof(uint32_t));
100  		chunk += sizeof(uint32_t);
101  		memcpy(chunk, cs->function, fn_size);
102  		chunk += fn_size;
103  	
104  		/* timestamp */
105  		memcpy(chunk, timestamp, sizeof(struct timespec));
106  		chunk += sizeof(struct timespec);
107  	
108  		/* log message length */
109  		msg_len_pt = chunk;
110  		chunk += sizeof(uint32_t);
111  	
112  		/* log message */
113  		msg_len = qb_vsnprintf_serialize(chunk, t->max_line_length, cs->format, ap);
114  		if (msg_len >= t->max_line_length) {
115  		    chunk = msg_len_pt + sizeof(uint32_t); /* Reset */
116  	
117  		    /* Leave this at QB_LOG_MAX_LEN so as not to overflow the blackbox */
118  		    msg_len = qb_vsnprintf_serialize(chunk, QB_LOG_MAX_LEN,
119  			"Log message too long to be stored in the blackbox.  "\
120  			"Maximum is QB_LOG_MAX_LEN" , ap);
121  		}
122  	
123  		actual_size += msg_len;
124  	
125  		/* now that we know the length, write it
126  		 */
127  		memcpy(msg_len_pt, &msg_len, sizeof(uint32_t));
128  	
129  		(void)qb_rb_chunk_commit(t->instance, actual_size);
130  	}
131  	
132  	static void
133  	_blackbox_close(int32_t target)
134  	{
135  		struct qb_log_target *t = qb_log_target_get(target);
136  	
137  		qb_rb_close(qb_rb_lastref_and_ret(
138  			(struct qb_ringbuffer_s **) &t->instance
139  		));
140  	}
141  	
142  	int32_t
143  	qb_log_blackbox_open(struct qb_log_target *t)
144  	{
145  		if (t->size < 1024) {
146  			return -EINVAL;
147  		}
148  		snprintf(t->filename, PATH_MAX, "%s-%d-blackbox", t->name, getpid());
149  	
150  		t->instance = qb_rb_open(t->filename, t->size,
151  					 QB_RB_FLAG_CREATE | QB_RB_FLAG_OVERWRITE, 0);
152  		if (t->instance == NULL) {
153  			return -errno;
154  		}
155  	
156  		t->logger = NULL;
157  		t->vlogger = _blackbox_vlogger;
158  		t->reload = _blackbox_reload;
159  		t->close = _blackbox_close;
160  		return 0;
161  	}
162  	
163  	/*
164  	 * This is designed to look as much like the ringbuffer header
165  	 * as possible so that we can distinguish an old RB dump
166  	 * from a new one with this header.
167  	 */
168  	
169  	struct _blackbox_file_header {
170  		uint32_t word_size;
171  		uint32_t read_pt;
172  		uint32_t write_pt;
173  		uint32_t version;
174  		uint32_t hash;
175  	} __attribute__((packed));
176  	
177  	/* Values we expect for a 'new' header */
178  	#define QB_BLACKBOX_HEADER_WORDSIZE 0
179  	#define QB_BLACKBOX_HEADER_READPT   0xCCBBCCBB
180  	#define QB_BLACKBOX_HEADER_WRITEPT  0xBBCCBBCC
181  	#define QB_BLACKBOX_HEADER_VERSION  2
182  	#define QB_BLACKBOX_HEADER_HASH     0
183  	
184  	ssize_t
185  	qb_log_blackbox_write_to_file(const char *filename)
186  	{
187  		ssize_t written_size = 0;
188  		struct qb_log_target *t;
189  		struct _blackbox_file_header header;
190  		int fd = open(filename, O_CREAT | O_RDWR, 0700);
191  	
192  		if (fd < 0) {
193  			return -errno;
194  		}
195  	
196  		/* Write header, so we know this is a 'new' format blackbox */
197  		header.word_size = QB_BLACKBOX_HEADER_WORDSIZE;
198  		header.read_pt   = QB_BLACKBOX_HEADER_READPT;
199  		header.write_pt  = QB_BLACKBOX_HEADER_WRITEPT;
200  		header.version   = QB_BLACKBOX_HEADER_VERSION;
201  		header.hash      = QB_BLACKBOX_HEADER_HASH;
202  		written_size = write(fd, &header, sizeof(header));
203  		if (written_size < sizeof(header)) {
204  			close(fd);
205  			return written_size;
206  		}
207  	
208  		t = qb_log_target_get(QB_LOG_BLACKBOX);
209  		if (t->instance) {
210  			written_size += qb_rb_write_to_file(t->instance, fd);
211  		} else {
212  			written_size = -ENOENT;
213  		}
214  		close(fd);
215  	
216  		return written_size;
217  	}
218  	
219  	int
220  	qb_log_blackbox_print_from_file(const char *bb_filename)
221  	{
222  		qb_ringbuffer_t *instance;
223  		ssize_t bytes_read;
224  		int max_size = 2 * QB_LOG_MAX_LEN;
225  		char *chunk;
226  		int fd;
227  		int err = 0;
228  		int saved_errno;
229  		struct _blackbox_file_header header;
230  		int have_timespecs = 0;
231  		char time_buf[64];
232  	
233  		fd = open(bb_filename, 0);
(1) Event cond_false: Condition "fd < 0", taking false branch.
234  		if (fd < 0) {
235  			saved_errno = errno;
236  			qb_util_perror(LOG_ERR, "qb_log_blackbox_print_from_file");
237  			return -saved_errno;
(2) Event if_end: End of if statement.
238  		}
239  	
240  		/* Read the header. If it looks like one of ours then
241  		   we know we have hi-res timestamps */
242  		err = read(fd, &header, sizeof(header));
(3) Event cond_false: Condition "err < 20UL /* sizeof (header) */", taking false branch.
243  		if (err < sizeof(header)) {
244  			saved_errno = errno;
245  			close(fd);
246  			return -saved_errno;
(4) Event if_end: End of if statement.
247  		}
248  	
(5) Event cond_true: Condition "header.word_size == 0", taking true branch.
(6) Event cond_true: Condition "header.read_pt == 3434859707U", taking true branch.
(7) Event cond_true: Condition "header.write_pt == 3150756812U", taking true branch.
(8) Event cond_true: Condition "header.version == 2", taking true branch.
(9) Event cond_true: Condition "header.hash == 0", taking true branch.
249  		if (header.word_size == QB_BLACKBOX_HEADER_WORDSIZE &&
250  		    header.read_pt == QB_BLACKBOX_HEADER_READPT &&
251  		    header.write_pt == QB_BLACKBOX_HEADER_WRITEPT &&
252  		    header.version == QB_BLACKBOX_HEADER_VERSION &&
253  		    header.hash == QB_BLACKBOX_HEADER_HASH) {
254  			have_timespecs = 1;
(10) Event if_fallthrough: Falling through to end of if statement.
255  		} else {
256  			(void)lseek(fd, 0, SEEK_SET);
(11) Event if_end: End of if statement.
257  		}
258  	
259  	
(12) Event tainted_return_value: Function "qb_rb_create_from_file" returns tainted data. [details]
(13) Event var_assign: Assigning: "instance" = "qb_rb_create_from_file(fd, 0U)", which taints "instance".
Also see events: [tainted_data][remediation]
260  		instance = qb_rb_create_from_file(fd, 0);
261  		close(fd);
(14) Event cond_false: Condition "instance == NULL", taking false branch.
262  		if (instance == NULL) {
263  			return -EIO;
(15) Event if_end: End of if statement.
264  		}
265  		chunk = malloc(max_size);
(16) Event cond_false: Condition "!chunk", taking false branch.
266  		if (!chunk) {
267  			goto cleanup;
(17) Event if_end: End of if statement.
268  		}
269  	
270  		do {
271  			char *ptr;
272  			uint32_t lineno;
273  			uint32_t tags;
274  			uint8_t priority;
275  			uint32_t fn_size;
276  			char *function;
277  			uint32_t len;
278  			struct timespec timestamp;
279  			time_t time_sec;
280  			uint32_t msg_len;
281  			struct tm *tm;
282  			char message[QB_LOG_MAX_LEN];
283  	
(18) Event tainted_data: Passing tainted expression "instance->shared_hdr" to "qb_rb_chunk_read", which uses it as a divisor or modulus. [details]
(19) Event remediation: Ensure that tainted values are properly sanitized, by checking that their values are within a permissible range.
Also see events: [tainted_return_value][var_assign]
284  			bytes_read = qb_rb_chunk_read(instance, chunk, max_size, 0);
285  	
286  			if (bytes_read >= 0 && bytes_read < BB_MIN_ENTRY_SIZE) {
287  				printf("ERROR Corrupt file: blackbox header too small.\n");
288  				err = -1;
289  				goto cleanup;
290  			} else if (bytes_read < 0) {
291  				errno = -bytes_read;
292  				perror("ERROR: qb_rb_chunk_read failed");
293  				err = -EIO;
294  				goto cleanup;
295  			}
296  			ptr = chunk;
297  	
298  			/* lineno */
299  			memcpy(&lineno, ptr, sizeof(uint32_t));
300  			ptr += sizeof(uint32_t);
301  	
302  			/* tags */
303  			memcpy(&tags, ptr, sizeof(uint32_t));
304  			ptr += sizeof(uint32_t);
305  	
306  			/* priority */
307  			memcpy(&priority, ptr, sizeof(uint8_t));
308  			ptr += sizeof(uint8_t);
309  	
310  			/* function size & name */
311  			memcpy(&fn_size, ptr, sizeof(uint32_t));
312  			if ((fn_size + BB_MIN_ENTRY_SIZE) > bytes_read) {
313  	#ifndef S_SPLINT_S
314  				printf("ERROR Corrupt file: fn_size way too big %" PRIu32 "\n", fn_size);
315  				err = -EIO;
316  	#endif /* S_SPLINT_S */
317  				goto cleanup;
318  			}
319  			if (fn_size <= 0) {
320  	#ifndef S_SPLINT_S
321  				printf("ERROR Corrupt file: fn_size negative %" PRIu32 "\n", fn_size);
322  				err = -EIO;
323  	#endif /* S_SPLINT_S */
324  				goto cleanup;
325  			}
326  			ptr += sizeof(uint32_t);
327  	
328  			function = ptr;
329  			ptr += fn_size;
330  	
331  			/* timestamp size & content */
332  			if (have_timespecs) {
333  				memcpy(&timestamp, ptr, sizeof(struct timespec));
334  				ptr += sizeof(struct timespec);
335  				time_sec = timestamp.tv_sec;
336  			} else {
337  				memcpy(&time_sec, ptr, sizeof(time_t));
338  				ptr += sizeof(time_t);
339  				timestamp.tv_nsec = 0LL;
340  			}
341  	
342  			tm = localtime(&time_sec);
343  			if (tm) {
344  				int slen = strftime(time_buf,
345  						    sizeof(time_buf), "%b %d %T",
346  						    tm);
347  				snprintf(time_buf+slen, sizeof(time_buf) - slen, ".%03llu", timestamp.tv_nsec/QB_TIME_NS_IN_MSEC);
348  			} else {
349  				snprintf(time_buf, sizeof(time_buf), "%ld",
350  					 (long int)time_sec);
351  			}
352  			/* message length */
353  			memcpy(&msg_len, ptr, sizeof(uint32_t));
354  			if (msg_len > QB_LOG_MAX_LEN || msg_len <= 0) {
355  	#ifndef S_SPLINT_S
356  				printf("ERROR Corrupt file: msg_len out of bounds %" PRIu32 "\n", msg_len);
357  				err = -EIO;
358  	#endif /* S_SPLINT_S */
359  				goto cleanup;
360  			}
361  	
362  			ptr += sizeof(uint32_t);
363  	
364  			/* message content */
365  			len = qb_vsnprintf_deserialize(message, QB_LOG_MAX_LEN, ptr);
366  			assert(len > 0);
367  			message[len] = '\0';
368  			len--;
369  			while (len > 0 && (message[len] == '\n' || message[len] == '\0')) {
370  				message[len] = '\0';
371  				len--;
372  			}
373  	
374  			printf("%-7s %s %s(%u):%u: %s\n",
375  			       qb_log_priority2str(priority),
376  			       time_buf, function, lineno, tags, message);
377  	
378  		} while (bytes_read > BB_MIN_ENTRY_SIZE);
379  	
380  	cleanup:
381  		qb_rb_close(instance);
382  		free(chunk);
383  		return err;
384  	}
385