Commit 74f2b9e4 authored by Peter Sanford's avatar Peter Sanford Committed by Alex Birch

Fix use after free and leak in get_arg_values

Previously get_arg_values was returning a vector of uint64_t values
that could be passed directly to printf(3). For string values
get_arg_values was returning a pointer to a char*. For some cases it
was attempting to handle freeing the char* memory via a stack
allocated std::vector. Unfortunately, this was stack allocated in
get_arg_values so the char* data would get freed before it was used in
the subsequent call to printf().

In other cases get_arg_values was not freeing char* values and was
leaking memory (probe, stack, and ustack).

get_arg_values() now returns a vector of objects of type IPrintable
instead of uint64_t values. Each object has a method .value() that
returns the uint64_t value usable by printf(). For strings this allows
us to keep around the original std::string until after we've called
printf(), so we don't need to strdup() anymore.

Fixes #194
parent 7b87a11a
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "bpforc.h" #include "bpforc.h"
#include "bpftrace.h" #include "bpftrace.h"
#include "attached_probe.h" #include "attached_probe.h"
#include "printf.h"
#include "triggers.h" #include "triggers.h"
#include "resolve_cgroupid.h" #include "resolve_cgroupid.h"
...@@ -263,7 +264,7 @@ void perf_event_printer(void *cb_cookie, void *data, int size) ...@@ -263,7 +264,7 @@ void perf_event_printer(void *cb_cookie, void *data, int size)
auto id = printf_id - asyncactionint(AsyncAction::syscall); auto id = printf_id - asyncactionint(AsyncAction::syscall);
auto fmt = std::get<0>(bpftrace->system_args_[id]).c_str(); auto fmt = std::get<0>(bpftrace->system_args_[id]).c_str();
auto args = std::get<1>(bpftrace->system_args_[id]); auto args = std::get<1>(bpftrace->system_args_[id]);
std::vector<uint64_t> arg_values = bpftrace->get_arg_values(args, arg_data); auto arg_values = bpftrace->get_arg_values(args, arg_data);
char buffer [255]; char buffer [255];
...@@ -273,30 +274,30 @@ void perf_event_printer(void *cb_cookie, void *data, int size) ...@@ -273,30 +274,30 @@ void perf_event_printer(void *cb_cookie, void *data, int size)
system(fmt); system(fmt);
break; break;
case 1: case 1:
snprintf(buffer, 255, fmt, arg_values.at(0)); snprintf(buffer, 255, fmt, arg_values.at(0)->value());
system(buffer); system(buffer);
break; break;
case 2: case 2:
snprintf(buffer, 255, fmt, arg_values.at(0), arg_values.at(1)); snprintf(buffer, 255, fmt, arg_values.at(0)->value(), arg_values.at(1)->value());
system(buffer); system(buffer);
break; break;
case 3: case 3:
snprintf(buffer, 255, fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2)); snprintf(buffer, 255, fmt, arg_values.at(0)->value(), arg_values.at(1)->value(), arg_values.at(2)->value());
system(buffer); system(buffer);
break; break;
case 4: case 4:
snprintf(buffer, 255, fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2), snprintf(buffer, 255, fmt, arg_values.at(0)->value(), arg_values.at(1)->value(), arg_values.at(2)->value(),
arg_values.at(3)); arg_values.at(3)->value());
system(buffer); system(buffer);
break; break;
case 5: case 5:
snprintf(buffer, 255, fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2), snprintf(buffer, 255, fmt, arg_values.at(0)->value(), arg_values.at(1)->value(), arg_values.at(2)->value(),
arg_values.at(3), arg_values.at(4)); arg_values.at(3)->value(), arg_values.at(4)->value());
system(buffer); system(buffer);
break; break;
case 6: case 6:
snprintf(buffer, 255, fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2), snprintf(buffer, 255, fmt, arg_values.at(0)->value(), arg_values.at(1)->value(), arg_values.at(2)->value(),
arg_values.at(3), arg_values.at(4), arg_values.at(5)); arg_values.at(3)->value(), arg_values.at(4)->value(), arg_values.at(5)->value());
system(buffer); system(buffer);
break; break;
default: default:
...@@ -309,7 +310,7 @@ void perf_event_printer(void *cb_cookie, void *data, int size) ...@@ -309,7 +310,7 @@ void perf_event_printer(void *cb_cookie, void *data, int size)
// printf // printf
auto fmt = std::get<0>(bpftrace->printf_args_[printf_id]).c_str(); auto fmt = std::get<0>(bpftrace->printf_args_[printf_id]).c_str();
auto args = std::get<1>(bpftrace->printf_args_[printf_id]); auto args = std::get<1>(bpftrace->printf_args_[printf_id]);
std::vector<uint64_t> arg_values = bpftrace->get_arg_values(args, arg_data); auto arg_values = bpftrace->get_arg_values(args, arg_data);
switch (args.size()) switch (args.size())
{ {
...@@ -317,38 +318,35 @@ void perf_event_printer(void *cb_cookie, void *data, int size) ...@@ -317,38 +318,35 @@ void perf_event_printer(void *cb_cookie, void *data, int size)
printf(fmt); printf(fmt);
break; break;
case 1: case 1:
printf(fmt, arg_values.at(0)); printf(fmt, arg_values.at(0)->value());
break; break;
case 2: case 2:
printf(fmt, arg_values.at(0), arg_values.at(1)); printf(fmt, arg_values.at(0)->value(), arg_values.at(1)->value());
break; break;
case 3: case 3:
printf(fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2)); printf(fmt, arg_values.at(0)->value(), arg_values.at(1)->value(), arg_values.at(2)->value());
break; break;
case 4: case 4:
printf(fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2), printf(fmt, arg_values.at(0)->value(), arg_values.at(1)->value(), arg_values.at(2)->value(),
arg_values.at(3)); arg_values.at(3)->value());
break; break;
case 5: case 5:
printf(fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2), printf(fmt, arg_values.at(0)->value(), arg_values.at(1)->value(), arg_values.at(2)->value(),
arg_values.at(3), arg_values.at(4)); arg_values.at(3)->value(), arg_values.at(4)->value());
break; break;
case 6: case 6:
printf(fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2), printf(fmt, arg_values.at(0)->value(), arg_values.at(1)->value(), arg_values.at(2)->value(),
arg_values.at(3), arg_values.at(4), arg_values.at(5)); arg_values.at(3)->value(), arg_values.at(4)->value(), arg_values.at(5)->value());
break; break;
default: default:
abort(); abort();
} }
} }
std::vector<uint64_t> BPFtrace::get_arg_values(std::vector<Field> args, uint8_t* arg_data) std::vector<std::unique_ptr<IPrintable>> BPFtrace::get_arg_values(std::vector<Field> args, uint8_t* arg_data)
{ {
std::vector<uint64_t> arg_values; std::vector<std::unique_ptr<IPrintable>> arg_values;
std::vector<std::unique_ptr<char>> resolved_symbols;
std::vector<std::unique_ptr<char>> resolved_usernames;
char *name;
for (auto arg : args) for (auto arg : args)
{ {
switch (arg.type.type) switch (arg.type.type)
...@@ -357,54 +355,52 @@ std::vector<uint64_t> BPFtrace::get_arg_values(std::vector<Field> args, uint8_t* ...@@ -357,54 +355,52 @@ std::vector<uint64_t> BPFtrace::get_arg_values(std::vector<Field> args, uint8_t*
switch (arg.type.size) switch (arg.type.size)
{ {
case 8: case 8:
arg_values.push_back(*(uint64_t*)(arg_data+arg.offset)); arg_values.emplace_back(new PrintableInt(*(uint64_t*)(arg_data+arg.offset)));
break; break;
case 4: case 4:
arg_values.push_back(*(uint32_t*)(arg_data+arg.offset)); arg_values.emplace_back(new PrintableInt(*(uint32_t*)(arg_data+arg.offset)));
break; break;
case 2: case 2:
arg_values.push_back(*(uint16_t*)(arg_data+arg.offset)); arg_values.emplace_back(new PrintableInt(*(uint16_t*)(arg_data+arg.offset)));
break; break;
case 1: case 1:
arg_values.push_back(*(uint8_t*)(arg_data+arg.offset)); arg_values.emplace_back(new PrintableInt(*(uint8_t*)(arg_data+arg.offset)));
break; break;
default: default:
abort(); abort();
} }
break; break;
case Type::string: case Type::string:
arg_values.push_back((uint64_t)(arg_data+arg.offset)); arg_values.emplace_back(new PrintableString(
std::string((char *) arg_data+arg.offset)));
break; break;
case Type::sym: case Type::sym:
resolved_symbols.emplace_back(strdup( arg_values.emplace_back(new PrintableString(
resolve_sym(*(uint64_t*)(arg_data+arg.offset)).c_str())); resolve_sym(*(uint64_t*)(arg_data+arg.offset)).c_str()));
arg_values.push_back((uint64_t)resolved_symbols.back().get());
break; break;
case Type::usym: case Type::usym:
resolved_symbols.emplace_back(strdup( arg_values.emplace_back(new PrintableString(
resolve_usym(*(uint64_t*)(arg_data+arg.offset), *(uint64_t*)(arg_data+arg.offset + 8)).c_str())); resolve_usym(*(uint64_t*)(arg_data+arg.offset), *(uint64_t*)(arg_data+arg.offset + 8)).c_str()));
arg_values.push_back((uint64_t)resolved_symbols.back().get());
break; break;
case Type::inet: case Type::inet:
name = strdup(resolve_inet(*(uint64_t*)(arg_data+arg.offset), *(uint64_t*)(arg_data+arg.offset+8)).c_str()); name = strdup(resolve_inet(*(uint64_t*)(arg_data+arg.offset), *(uint64_t*)(arg_data+arg.offset+8)).c_str());
arg_values.push_back((uint64_t)name); arg_values.push_back((uint64_t)name);
break; break;
case Type::username: case Type::username:
resolved_usernames.emplace_back(strdup( arg_values.emplace_back(new PrintableString(
resolve_uid(*(uint64_t*)(arg_data+arg.offset)).c_str())); resolve_uid(*(uint64_t*)(arg_data+arg.offset)).c_str()));
arg_values.push_back((uint64_t)resolved_usernames.back().get());
break; break;
case Type::probe: case Type::probe:
name = strdup(resolve_probe(*(uint64_t*)(arg_data+arg.offset)).c_str()); arg_values.emplace_back(new PrintableString(
arg_values.push_back((uint64_t)name); resolve_probe(*(uint64_t*)(arg_data+arg.offset)).c_str()));
break; break;
case Type::stack: case Type::stack:
name = strdup(get_stack(*(uint64_t*)(arg_data+arg.offset), false, 8).c_str()); arg_values.emplace_back(new PrintableString(
arg_values.push_back((uint64_t)name); get_stack(*(uint64_t*)(arg_data+arg.offset), false, 8).c_str()));
break; break;
case Type::ustack: case Type::ustack:
name = strdup(get_stack(*(uint64_t*)(arg_data+arg.offset), true, 8).c_str()); arg_values.emplace_back(new PrintableString(
arg_values.push_back((uint64_t)name); get_stack(*(uint64_t*)(arg_data+arg.offset), true, 8).c_str()));
break; break;
default: default:
abort(); abort();
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "ast.h" #include "ast.h"
#include "attached_probe.h" #include "attached_probe.h"
#include "imap.h" #include "imap.h"
#include "printf.h"
#include "struct.h" #include "struct.h"
#include "types.h" #include "types.h"
...@@ -68,7 +69,7 @@ public: ...@@ -68,7 +69,7 @@ public:
std::string extract_func_symbols_from_path(const std::string &path); std::string extract_func_symbols_from_path(const std::string &path);
std::string resolve_probe(uint64_t probe_id); std::string resolve_probe(uint64_t probe_id);
uint64_t resolve_cgroupid(const std::string &path); uint64_t resolve_cgroupid(const std::string &path);
std::vector<uint64_t> get_arg_values(std::vector<Field> args, uint8_t* arg_data); std::vector<std::unique_ptr<IPrintable>> get_arg_values(std::vector<Field> args, uint8_t* arg_data);
void add_param(const std::string &param); void add_param(const std::string &param);
bool is_numeric(std::string str); bool is_numeric(std::string str);
std::string get_param(int index); std::string get_param(int index);
......
...@@ -64,4 +64,14 @@ std::string verify_format_string(const std::string &fmt, std::vector<Field> args ...@@ -64,4 +64,14 @@ std::string verify_format_string(const std::string &fmt, std::vector<Field> args
return ""; return "";
} }
uint64_t PrintableString::value()
{
return (uint64_t)_value.c_str();
}
uint64_t PrintableInt::value()
{
return _value;
}
} // namespace bpftrace } // namespace bpftrace
#pragma once
#include <sstream> #include <sstream>
#include "ast.h" #include "ast.h"
...@@ -9,4 +11,30 @@ struct Field; ...@@ -9,4 +11,30 @@ struct Field;
std::string verify_format_string(const std::string &fmt, std::vector<Field> args); std::string verify_format_string(const std::string &fmt, std::vector<Field> args);
class IPrintable
{
public:
virtual ~IPrintable() { };
virtual uint64_t value() = 0;
};
class PrintableString : public virtual IPrintable
{
public:
PrintableString(std::string value) : _value(value) { }
uint64_t value();
private:
std::string _value;
};
class PrintableInt : public virtual IPrintable
{
public:
PrintableInt(uint64_t value) : _value(value) { }
uint64_t value();
private:
uint64_t _value;
};
} // namespace bpftrace } // namespace bpftrace
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment