[DynInst_API:] setting dyninst probes in firefox


Date: Fri, 27 Mar 2020 16:58:43 -0400
From: Stan Cox <scox@xxxxxxxxxx>
Subject: [DynInst_API:] setting dyninst probes in firefox
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;
}
[← Prev in Thread] Current Thread [Next in Thread→]