We are seeing a problem with stapdyn (the systemtap dyninst backend)
with dyninst 10.1.0 while setting a probe in firefox. I have attached
an admittingly quickly thrown together standalone program that shows the
same symptoms.
% DYNINSTAPI_RT_LIB=/usr/lib64/dyninst/libdyninstAPI_RT.so /work/scox/dyn/smoke-test/dynamic-static/mutator-pp.x -e /usr/lib64/firefox/firefox -f main -p 27654
Instruction jnle 0x9d(%rip)
type should be either COND_TAKEN or COND_NOT_TAKEN, but it is 5
From 4076d03 to 4076d03
mutator-pp.x: /builddir/build/BUILD/dyninst-10.1.0/dyninst-10.1.0/parseAPI/src/BoundFactCalculator.C:379: BoundFact* BoundFactsCalculator::Meet(Dyninst::Node::Ptr): Assertion `0' failed.
Aborted (core dumped)
Command line says to add a probe at firefox's main function, pid 27654
Setting DYNINST_DEBUG_PARSING=1 it seems the problems occur after:
Adding shared object libmozwayland.so, addr range 0x3ed9d000 to 0x3ed9d3b5
This is on Fedora 30:
Fedora release 30 (Thirty) x86_64
firefox-74.0-3.fc30.x86_64
/usr/lib64/firefox/libmozwayland.so
#include <cstdlib>
#include <cstdio>
#include <string>
#include <vector>
#include <iostream>
// dyninst libraries
#include <dyninst/BPatch.h>
#include <dyninst/BPatch_addressSpace.h>
#include <dyninst/BPatch_process.h>
#include <dyninst/BPatch_function.h>
#include <dyninst/BPatch_point.h>
#include <dyninst/BPatch_object.h>
using namespace std;
BPatch bpatch;
static void
usage (bool ok = false, string mssg = "")
{
if (!ok)
{
if (mssg.size() > 0)
cerr << mssg << endl;
cerr << "Usage mutator-pp [-static] [-dynamic] -e EXECUTABLE -f FUNCTION -a ADDRESS -p PID\n" << endl;
exit (1);
}
}
bool is_number(const std::string& s)
{
std::string::const_iterator it = s.begin();
while (it != s.end() && std::isdigit(*it)) ++it;
return !s.empty() && it == s.end();
}
// Create and insert a printf snippet
bool insert_printf_snippet(BPatch_addressSpace* app,
BPatch_object *obj,
std::vector<BPatch_point*>& points)
{
BPatch_image* appImage = app->getImage();
// Create the printf function call snippet
std::vector<BPatch_snippet*> printfArgs;
// BPatch_snippet* fmt = new BPatch_constExpr("In main\n");
BPatch_snippet* fmt = new BPatch_constExpr("dyninst probe: %s\n");
BPatch_snippet* arg1 = new BPatch_constExpr(obj->name().c_str());
printfArgs.push_back(fmt);
printfArgs.push_back(arg1);
// BPatch_variableExpr* var = appImage->findVariable("myCounter");
/*
if (!var)
{
fprintf(stderr, "Could not find 'myCounter' variable\n");
return false;
}
else
{
printfArgs.push_back(var);
}
*/
// Find the printf function
std::vector<BPatch_function*> printfFuncs;
appImage->findFunction("printf", printfFuncs);
if (printfFuncs.size() == 0)
{
fprintf(stderr, "Could not find printf\n");
return false;
}
// Construct a function call snippet
BPatch_funcCallExpr printfCall(*(printfFuncs[0]), printfArgs);
// Insert the snippet
if (!app->insertSnippet(printfCall, points))
{
fprintf(stderr, "insertSnippet failed\n");
return false;
}
return true;
}
int
main(int argc, char **argv)
{
long pid = 0;
BPatch_addressSpace *app_bin;
BPatch_addressSpace *aspace;
BPatch_image *image;
enum {dyn_static, dyn_dynamic} dyn_type;
int argn = 1;
string input_function;
string input_binary;
long input_address;
bool list_objects = false;
for (int argn = 1; argn < argc; argn++)
{
if (strncmp (argv[argn], "-l", 2) == 0)
list_objects = true;
else if (strncmp (argv[argn], "-h", 2) == 0)
usage ();
else if (strcmp (argv[argn], "-static") == 0)
dyn_type = dyn_static;
else if (strcmp (argv[argn], "-dynamic") == 0)
dyn_type = dyn_dynamic;
else if (strncmp (argv[argn], "-e", 2) == 0)
{
usage (++argn < argc, "-e is missing an EXECUTABLE");
input_binary = argv[argn];
}
else if (strncmp (argv[argn], "-f", 2) == 0)
{
usage (++argn < argc, "-f is missing a FUNCTION");
input_function = argv[argn];
}
else if (strncmp (argv[argn], "-a", 2) == 0)
{
char *p;
usage (++argn < argc, "-a is missing an ADDRESS");
// could use unsigned int x = std::stoul(s, nullptr, 16);
input_address = strtol(argv[argn], &p, 16);
usage (*p == 0, "-a has an invalid address");
}
else if (strncmp (argv[argn], "-p", 2) == 0)
{
usage (++argn < argc, "-p is missing a PID");
if (is_number (argv[argn]))
{
pid = atoi(argv[argn]);
usage (pid != 0, "-p was given an invalid PID");
}
}
else if (strncmp (argv[argn], "-", 1) == 0)
usage (0, "invalid option ");
else
usage (0, "invalid argument ");
}
usage (input_binary.size() > 0, "required option: -e EXECUTABLE");
if (pid > 0)
{
app_bin = bpatch.processAttach(NULL, pid);
dyn_type = dyn_dynamic;
}
else
{
const char* prog_argv[] = {input_binary.c_str(), "-v", "5", NULL};
if (dyn_type == dyn_dynamic)
app_bin = bpatch.processCreate(input_binary.c_str(), prog_argv);
else /* assume dyn_static */
app_bin = bpatch.openBinary(input_binary.c_str());
}
if (!app_bin)
{
cerr << "File not found: " << input_binary << endl;
usage (app_bin != 0, "EXECUTABLE not found");
}
aspace = app_bin;
image = aspace->getImage();
const char *inst_library = "libc.so.6";
if (!app_bin->loadLibrary (inst_library))
{
cerr << "Failed to open libc" << endl;
return EXIT_FAILURE;
}
// Read all of the objects in the process.
vector<BPatch_object *> objects;
image->getObjects(objects);
if (list_objects)
{
cout << "List of objects:" << endl;
for (BPatch_object *o : objects) // access by const reference
std::cout << o->name() << endl;
exit (0);
}
bool have_match;
Dyninst::Address dyn_address;
for (size_t i = 0; i < objects.size(); ++i)
{
vector<BPatch_point*> points;
// FUNCTION
if (input_function.size() > 0)
{
vector<BPatch_function*> fn_function;
image->findFunction(input_function.c_str(), fn_function);
if (fn_function.size() == 0) {
cerr << "No function " << input_function << endl;
exit(1);
}
std::vector<BPatch_point*>* fn_entry_point = fn_function[0]->findPoint(BPatch_entry);
fn_entry_point = fn_function[0]->findPoint(BPatch_entry);
if (!fn_entry_point || fn_entry_point->size() == 0) {
fprintf(stderr, "No entry point for function_name\n");
exit(1);
}
cout << objects[i]->name() << ' ' << std::hex << (*fn_entry_point)[0]->getAddress() << endl;
points.push_back ((*fn_entry_point)[0]);
have_match = true;
}
// ADDRESS
else if (input_address > 0)
{
// Convert the file offset to a memory address.
// dyn_address = objects[i]->fileOffsetToAddr(input_address);
// if (dyn_address == BPatch_object::E_OUT_OF_BOUNDS)
// continue;
dyn_address = input_address;
char input_base[PATH_MAX];
strcpy (input_base, input_binary.c_str());
cout << objects[i]->name() << ' ' << basename(input_base) << ' ' << std::hex << dyn_address << endl;
if (objects[i]->name() != input_base) // Only consider address for primary executable
continue;
// Turn the address into instrumentation points.
// There may be multiple results if Dyninst determined that multiple
// concrete functions have overlapping ranges. Rare, but possible.
objects[i]->findPoints(dyn_address, points);
if (!points.empty())
{
cout << "Found an instrumentation point at "
<< std::hex << dyn_address << " in " << input_binary << endl;
have_match = true;
}
else
continue;
}
// Check that the functions containing each point are actually
// instrumentable. Unfortunately, findPoints doesn't make any
// distinction, and insertSnippet doesn't tell us either, so this
// is the best way we have to let the user know.
vector<BPatch_point*> instrumentable_points;
for (size_t i = 0; i < points.size(); ++i)
if (points[i]->getFunction()->isInstrumentable())
instrumentable_points.push_back(points[i]);
else
cerr << "Couldn't instrument the function containing "
<< dyn_address << ", " << input_binary
<< "+" << input_address << endl;
points.swap(instrumentable_points);
// Create and insert printf snippet into main
// if (!insert_printf_snippet(aspace, objects[i], instrumentable_points))
// {
// fprintf(stderr, "insert_printf_snippet failed\n");
// exit(1);
// }
}
if (have_match == false)
{
for (size_t i = 0; i < objects.size(); ++i)
cerr << "Couldn't insert probe at " << std::hex << input_address << " in " << objects[i]->name() << endl;
usage();
}
if (dyn_type == dyn_static)
{
string exefile = input_binary;
std::size_t found;
found = exefile.rfind('/');
if (found != string::npos)
exefile = exefile.substr(found+1);
found = exefile.find_last_of(".");
if (found == string::npos)
exefile += "-mutated";
else
exefile = exefile.substr(0,found) + "-mutated" + exefile.substr(found);
cout << "Mutation done. Created " + exefile << endl;
BPatch_binaryEdit* appbined = dynamic_cast<BPatch_binaryEdit*>(app_bin);
appbined->writeFile(exefile.c_str());
}
else if (dyn_type == dyn_dynamic)
{
cout << "Mutation done. Running mutatee\n";
BPatch_process* appproc = dynamic_cast<BPatch_process*>(app_bin);
appproc->continueExecution();
appproc->detach(true);
}
return 0;
}
|