Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tracing: Use trace_seq_used() and seq_buf_used() instead of len

As the seq_buf->len will soon be +1 size when there's an overflow, we
must use trace_seq_used() or seq_buf_used() methods to get the real
length. This will prevent buffer overflow issues if just the len
of the seq_buf descriptor is used to copy memory.

Link: http://lkml.kernel.org/r/20141114121911.09ba3d38@gandalf.local.home

Reported-by: Petr Mladek <pmladek@suse.cz>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>

+42 -17
+19 -1
include/linux/trace_seq.h
··· 24 24 } 25 25 26 26 /** 27 + * trace_seq_used - amount of actual data written to buffer 28 + * @s: trace sequence descriptor 29 + * 30 + * Returns the amount of data written to the buffer. 31 + * 32 + * IMPORTANT! 33 + * 34 + * Use this instead of @s->seq.len if you need to pass the amount 35 + * of data from the buffer to another buffer (userspace, or what not). 36 + * The @s->seq.len on overflow is bigger than the buffer size and 37 + * using it can cause access to undefined memory. 38 + */ 39 + static inline int trace_seq_used(struct trace_seq *s) 40 + { 41 + return seq_buf_used(&s->seq); 42 + } 43 + 44 + /** 27 45 * trace_seq_buffer_ptr - return pointer to next location in buffer 28 46 * @s: trace sequence descriptor 29 47 * ··· 53 35 static inline unsigned char * 54 36 trace_seq_buffer_ptr(struct trace_seq *s) 55 37 { 56 - return s->buffer + s->seq.len; 38 + return s->buffer + seq_buf_used(&s->seq); 57 39 } 58 40 59 41 /**
+1 -1
kernel/trace/seq_buf.c
··· 328 328 if (s->len <= s->readpos) 329 329 return -EBUSY; 330 330 331 - len = s->len - s->readpos; 331 + len = seq_buf_used(s) - s->readpos; 332 332 if (cnt > len) 333 333 cnt = len; 334 334 ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt);
+11 -10
kernel/trace/trace.c
··· 944 944 { 945 945 int len; 946 946 947 - if (s->seq.len <= s->seq.readpos) 947 + if (trace_seq_used(s) <= s->seq.readpos) 948 948 return -EBUSY; 949 949 950 - len = s->seq.len - s->seq.readpos; 950 + len = trace_seq_used(s) - s->seq.readpos; 951 951 if (cnt > len) 952 952 cnt = len; 953 953 memcpy(buf, s->buffer + s->seq.readpos, cnt); ··· 4514 4514 trace_access_lock(iter->cpu_file); 4515 4515 while (trace_find_next_entry_inc(iter) != NULL) { 4516 4516 enum print_line_t ret; 4517 - int len = iter->seq.seq.len; 4517 + int save_len = iter->seq.seq.len; 4518 4518 4519 4519 ret = print_trace_line(iter); 4520 4520 if (ret == TRACE_TYPE_PARTIAL_LINE) { 4521 4521 /* don't print partial lines */ 4522 - iter->seq.seq.len = len; 4522 + iter->seq.seq.len = save_len; 4523 4523 break; 4524 4524 } 4525 4525 if (ret != TRACE_TYPE_NO_CONSUME) 4526 4526 trace_consume(iter); 4527 4527 4528 - if (iter->seq.seq.len >= cnt) 4528 + if (trace_seq_used(&iter->seq) >= cnt) 4529 4529 break; 4530 4530 4531 4531 /* ··· 4541 4541 4542 4542 /* Now copy what we have to the user */ 4543 4543 sret = trace_seq_to_user(&iter->seq, ubuf, cnt); 4544 - if (iter->seq.seq.readpos >= iter->seq.seq.len) 4544 + if (iter->seq.seq.readpos >= trace_seq_used(&iter->seq)) 4545 4545 trace_seq_init(&iter->seq); 4546 4546 4547 4547 /* ··· 4598 4598 break; 4599 4599 } 4600 4600 4601 - count = iter->seq.seq.len - save_len; 4601 + count = trace_seq_used(&iter->seq) - save_len; 4602 4602 if (rem < count) { 4603 4603 rem = 0; 4604 4604 iter->seq.seq.len = save_len; ··· 4682 4682 /* Copy the data into the page, so we can start over. */ 4683 4683 ret = trace_seq_to_buffer(&iter->seq, 4684 4684 page_address(spd.pages[i]), 4685 - iter->seq.seq.len); 4685 + trace_seq_used(&iter->seq)); 4686 4686 if (ret < 0) { 4687 4687 __free_page(spd.pages[i]); 4688 4688 break; 4689 4689 } 4690 4690 spd.partial[i].offset = 0; 4691 - spd.partial[i].len = iter->seq.seq.len; 4691 + spd.partial[i].len = trace_seq_used(&iter->seq); 4692 4692 4693 4693 trace_seq_init(&iter->seq); 4694 4694 } ··· 5689 5689 cnt = ring_buffer_read_events_cpu(trace_buf->buffer, cpu); 5690 5690 trace_seq_printf(s, "read events: %ld\n", cnt); 5691 5691 5692 - count = simple_read_from_buffer(ubuf, count, ppos, s->buffer, s->seq.len); 5692 + count = simple_read_from_buffer(ubuf, count, ppos, 5693 + s->buffer, trace_seq_used(s)); 5693 5694 5694 5695 kfree(s); 5695 5696
+6 -3
kernel/trace/trace_events.c
··· 1044 1044 mutex_unlock(&event_mutex); 1045 1045 1046 1046 if (file) 1047 - r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->seq.len); 1047 + r = simple_read_from_buffer(ubuf, cnt, ppos, 1048 + s->buffer, trace_seq_used(s)); 1048 1049 1049 1050 kfree(s); 1050 1051 ··· 1211 1210 trace_seq_init(s); 1212 1211 1213 1212 print_subsystem_event_filter(system, s); 1214 - r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->seq.len); 1213 + r = simple_read_from_buffer(ubuf, cnt, ppos, 1214 + s->buffer, trace_seq_used(s)); 1215 1215 1216 1216 kfree(s); 1217 1217 ··· 1267 1265 trace_seq_init(s); 1268 1266 1269 1267 func(s); 1270 - r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->seq.len); 1268 + r = simple_read_from_buffer(ubuf, cnt, ppos, 1269 + s->buffer, trace_seq_used(s)); 1271 1270 1272 1271 kfree(s); 1273 1272
+4 -1
kernel/trace/trace_functions_graph.c
··· 1153 1153 return ret; 1154 1154 } 1155 1155 1156 + if (trace_seq_has_overflowed(s)) 1157 + goto out; 1158 + 1156 1159 /* Strip ending newline */ 1157 1160 if (s->buffer[s->seq.len - 1] == '\n') { 1158 1161 s->buffer[s->seq.len - 1] = '\0'; ··· 1163 1160 } 1164 1161 1165 1162 trace_seq_puts(s, " */\n"); 1166 - 1163 + out: 1167 1164 return trace_handle_return(s); 1168 1165 } 1169 1166
+1 -1
kernel/trace/trace_seq.c
··· 30 30 #define TRACE_SEQ_BUF_LEFT(s) seq_buf_buffer_left(&(s)->seq) 31 31 32 32 /* How much buffer is written? */ 33 - #define TRACE_SEQ_BUF_USED(s) min((s)->seq.len, (unsigned int)(PAGE_SIZE - 1)) 33 + #define TRACE_SEQ_BUF_USED(s) seq_buf_used(&(s)->seq) 34 34 35 35 /* 36 36 * trace_seq should work with being initialized with 0s.