diff options
author | Josh Stone <jistone@redhat.com> | 2009-12-21 18:26:47 -0800 |
---|---|---|
committer | Josh Stone <jistone@redhat.com> | 2009-12-21 18:44:02 -0800 |
commit | 63db23df87bf6408c0947053288b771d863ecf36 (patch) | |
tree | 6f32163dc8db6da09000d589c590e5fdb9a4e8b1 /translate.cxx | |
parent | 0f4e0b6fed0ee334cf33073be381ee22925b59a7 (diff) | |
download | systemtap-steved-63db23df87bf6408c0947053288b771d863ecf36.tar.gz systemtap-steved-63db23df87bf6408c0947053288b771d863ecf36.tar.xz systemtap-steved-63db23df87bf6408c0947053288b771d863ecf36.zip |
PR11112: Check the full %m/M buffer, and limit the length
We already had code in place to try a deref on the requested memory
buffer, but it was missing the static-precision case. Thus, it was
possible to craft an address that would pass the check on the first byte
but would pagefault at the end of the buffer.
While we're at it, we should also be limiting the number of bytes in
such a read, so even legitimately-huge buffers won't chew up kernel
time. I've arbitrarily chosen 1024 as the limit, but we can revisit
that later. (see also PR10490)
TODO: we need a reliable testcase where a starting address is valid but
the end address is bogus. In PR11112, the reproducer was using a huge
precision to run off the heap, but we need something that will
consistently work even with <1024 length.
Diffstat (limited to 'translate.cxx')
-rw-r--r-- | translate.cxx | 39 |
1 files changed, 26 insertions, 13 deletions
diff --git a/translate.cxx b/translate.cxx index 8c624a2f..81b8bef5 100644 --- a/translate.cxx +++ b/translate.cxx @@ -4264,20 +4264,33 @@ c_unparser::visit_print_format (print_format* e) if (components[i].prectype == print_format::prec_dynamic) prec_ix = arg_ix++; - /* Generate a noop call to deref_buffer for %m. */ + /* %m and %M need special care for digging into memory. */ if (components[i].type == print_format::conv_memory - || components[i].type == print_format::conv_memory_hex) { - this->probe_or_function_needs_deref_fault_handler = true; - o->newline() << "deref_buffer (0, " << tmp[arg_ix].value() << ", "; - if (prec_ix == -1) - if (width_ix != -1) - prec_ix = width_ix; - if (prec_ix != -1) - o->line() << tmp[prec_ix].value(); - else - o->line() << "1"; - o->line() << ");"; - } + || components[i].type == print_format::conv_memory_hex) + { + string mem_size; + if (prec_ix != -1) + mem_size = tmp[prec_ix].value(); + else if (components[i].prectype == print_format::prec_static && + components[i].precision > 0) + mem_size = lex_cast(components[i].precision) + "LL"; + else + mem_size = "1LL"; + + /* Limit how much can be printed at a time. (see also PR10490) */ + o->newline() << "if (" << mem_size << " > 1024) {"; + o->newline(1) << "snprintf(c->error_buffer, sizeof(c->error_buffer), " + << "\"%lld is too many bytes for a memory dump\", " + << mem_size << ");"; + o->newline() << "c->last_error = c->error_buffer;"; + o->newline() << "goto out;"; + o->newline(-1) << "}"; + + /* Generate a noop call to deref_buffer. */ + this->probe_or_function_needs_deref_fault_handler = true; + o->newline() << "deref_buffer (0, " << tmp[arg_ix].value() << ", " + << mem_size << " ?: 1LL);"; + } ++arg_ix; } |