1

I am looking for a way to be able to redefine a set of POSIX functions but then end the redefinition with a call to the original function. The idea is that I am trying to create a layer that can restrict what OS API's can be called depending on which "profile" is active. This "profile" determines what set of functions are allowed and any not specified should not be used.

For example, if in one profile I am not allowed to use strcpy, I would like to be able to either cause a compile time error (via static_assert) or print something to the screen saying "strcpy is not allowed in this profile" such as below:

MY_string.h

#include <string.h>

char *strcpy(char *restrict s1, const char *restrict s2)
{
#if defined(PROFILE_PASS_THROUGH)
    printf("strcpy is not allowed in this profile\n");
    return strcpy(s1, s2);
#elif defined(PROFILE_ERROR)
    static_assesrt(0, "strcpy is not allowed in this profile\n");
    return 0;
#else
    return strcpy(s1, s2);
#endif
}

So that way within main.cpp I can use MY_string.h

#define PROFILE_PASS_THROUGH
#include "MY_string.h"

int main()
{
    char temp1[10];
    char temp2[10];
    sprintf(temp2, "Testing");
    if (0 = strcpy(temp1, temp2))
    {
        printf("temp1 is %s\n", temp1);
    }
    return 0;
}

Now I realize that the code I have written above will not compile properly due to the redefinition of strcpy, but is there a way to allow this sort of functionality without playing around with macros or creating my own standard c and c++ libraries?

qubit
  • 25
  • 1
  • 6
  • 2
    What sort of "profile" and why are you "not allowed to use `strcpy`" within it? – Lightness Races in Orbit May 02 '13 at 15:28
  • @AK4749: `somenamespace::strcpy` is a different symbol than `strcpy`. Your suggestion does not address the question as posed. – Lightness Races in Orbit May 02 '13 at 15:29
  • @LightnessRacesinOrbit I guess that makes sense, but a `using` declaration (normally horribly against, but hey he's overriding standard functions) should take care of that right? I'm just thinking out of my ass here, I don't know if I'm right – im so confused May 02 '13 at 15:30
  • aka bottom of his `MY_String.h`, put a `using myspace::strcpy` – im so confused May 02 '13 at 15:32
  • then to override he just has to prefix with std:: – im so confused May 02 '13 at 15:33
  • @LightnessRacesinOrbit I was just using strcpy as an example. Basically I am given a list of POSIX functions that are not allowed to be used and I have to find a way to restrict their usage for others. – qubit May 02 '13 at 15:33
  • I think my idea works actually then – im so confused May 02 '13 at 15:34
  • 1
    If users are OS users, you might be able to simply setup the file permissions so certain profiles can't even access the certain headers/library files... I don't know if that would work, but it seems to me any *programmatic* solution could easily be bypassed by just including the actual header and linking to the actual library file. – void ptr May 02 '13 at 15:42
  • I asked a similar question about [overriding a function in C](http://stackoverflow.com/questions/617554/override-a-function-call-in-c), maybe some of the solutions posted there might provide some insight to your problem. – dreamlax May 02 '13 at 15:45
  • @Joe: Why? For what purpose? – Lightness Races in Orbit May 02 '13 at 15:47
  • @AK4749: No, because his users could trivially work around it. `::strcpy(...)`/`std::strcpy(...)` (depending on the header used) _oops_! Also, your way creates ambiguous function calls. – Lightness Races in Orbit May 02 '13 at 15:47
  • @LightnessRacesinOrbit Because there may be a situation where I want a process to not use TCP sockets so I could say that listen() and accept() are not allowed in the profile. Again that's just an example. – qubit May 02 '13 at 15:56
  • @Joe I don't understand why you should have that control. – Lightness Races in Orbit May 02 '13 at 16:23

7 Answers7

3
  1. You can write a preprocessor that changes calls to the standard routine to calls to your own routine. Such a preprocessor might be complicated, depending whether you need to recognize the full C++ grammar to distinguish calls using name spaces and so on or you can get away with more casual recognition of the calls.

  2. You can link with your own library, producing a relocatable object module with resolved names stripped. Your library would contain routines with the standard names, such as strcpy, that execute whatever code you desire and call other names, such as Mystrcpy. The object module produced by this is then linked with a second library and with the standard library. The second library contains routines with those names, such as Mystrcpy, that call the original library names strcpy. The details for doing this are of course dependent on your linker. The goal is to have a chain like this: Original code calls strcpy. This is resolved to the version of strcpy in the first library. That version calls Mystrcpy. Mystrcpy calls the standard library strcpy.

  3. You can compile to assembly and edit the names in the assembly so that your routines are called instead of the standard library routines.

  4. On some systems, you can use dlsym and other functions defined in <dlfcn.h> to load the dynamic library that contains the standard implementations and to call them via pointers returned by dlsym instead of by the usual names in source code.

  5. The GCC linker has a --wrap switch that resolves calls to foo to your routine __wrap_foo and resolves calls to __real_foo (which you would use in your implementation) to the real foo.

See also Intercepting Arbitrary Functions on Windows, UNIX, and Macintosh OS X Platforms.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
0

No, cannot be done in C++. What you want is more akin to a LISP (or derivative) language, where you can grab the slot for an existing function and 'override it in place', potentially punting back to the original implementation.

Scott Jones
  • 2,880
  • 13
  • 19
  • How would you disambiguate at the call site? You'd probably have to change it. So as a general technique that's non-intrusive in existing code, it's not ideal. – Scott Jones May 02 '13 at 15:28
  • either drop a local using declaration or prefix the call, I suppose, but I agree with you, he's definitely not doing something ideal – im so confused May 02 '13 at 15:29
0

Typical way of doing is on Unix is via LD_PRELOAD, example (Unix) below proxies a function call, malloc in particular (full example):

/**
 * malloc() direct call
 */
inline void * libc_malloc(size_t size)
{
  typedef void* (*malloc_func_t)(size_t);
  static malloc_func_t malloc_func = (malloc_func_t) dlsym(RTLD_NEXT, "malloc");

  return malloc_func(size);
}
bobah
  • 18,364
  • 2
  • 37
  • 70
  • I would say... not quite. With `LD_PRELOAD` you fully replace the function, whilst the OP would like to call the original function afterward. – Matthieu M. May 02 '13 at 15:52
  • @MatthieuM. - from the wrapper you can either call or not call the original method, or you can wrap not. flexible – bobah May 02 '13 at 16:02
0

In your MY_String.h:

... blah blah
using mynamespace::strcpy;
#endif // header guard or maybe not there if using pragma

then all strcpys that are not prefixed with std:: will use yours. If you REALLY want to ban them, grep and take a shotgun with you when you find the person who used it.

im so confused
  • 2,091
  • 1
  • 16
  • 25
  • What about situations where there the original function does not have a prefix? I'm working with a subset of the POSIX functions so for example mq_open does not seem to be in any namespace. – qubit May 02 '13 at 15:42
  • @Joe then I believe (BELIEVE, these other guys know more) that they have no way to override it directly in the code with a prefix – im so confused May 02 '13 at 15:43
  • Functions that are not in a namespace are in the global namespace, e.g. `::mq_open()`. – Fred Larson May 02 '13 at 15:46
  • @FredLarson If that's the case though would I be able to call the actual ::mq_open from within my implementation of myspace::mq_open? – qubit May 02 '13 at 15:53
  • @Joe: Yes. But see the problems noted by Lightness Races in Orbit in comments on the question. – Fred Larson May 02 '13 at 15:57
0

If using some recent GCC (e.g. version 4.7 or newer) you could also write a GCC plugin or a GCC extension in MELT to replace every call to strcpy to your own mystrcpy. This probably will take you some work (perhaps days, not hours) but has the enormous advantage to work inside the compiler, on the GCC compiler's internal representations (Gimple). So it will be done even after inlining, etc. And since you extend the compiler, you can tailor its behavior to what you want.

MELT is a domain specific language to extend GCC. It is designed for such tasks.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
0

You cannot avoid these functions to be called.

A C++ program can do anything it wants, it could have some code that loads the strcpy symbol from libc and runs it. If a malicious developer want to call that function, you have no way to avoid it. To do that you'd need to run the C++ code in some special environment (in a sandbox, or virtual machine), but I'm afraid such technology is not available.

If you trust the developers, and you're just looking for a way to remind them not to call certain functions, then there could be some solution.

One solution could be avoiding to #include libc headers (like cstring), and only include your own header files where you only declared the desired functions.

Another solution could be that of looking to the compiled executable in order to find out what functions are called, or to LD_PRELOAD a library that redefines (and thus overrides) standard functions to make them print a warning at runtime.

peoro
  • 25,562
  • 20
  • 98
  • 150
  • Ideally I do trust the developers. My way of reminding them right now is just giving them a compile time assert if they use the function (by definining the function as a macro). Problem is this is not a pretty solution. – qubit May 02 '13 at 15:58
  • @Joe: why isn't it a nice solution? If it's working I think it's fine. What you want to, after all, is a hack itself in my opinion. – peoro May 02 '13 at 15:59
  • True, I was just seeing if there was a better way since with the current way I'm doing it there is no way to pass through to the real function or have any run time indication that an "illegal" function has been used. – qubit May 02 '13 at 16:03
0

Here is how you would you change MY_string.h

#include <cstring>

namespace my_functions{
    char *strcpy(char *s1, const char *s2)
    {
#if defined(PROFILE_PASS_THROUGH)
        printf("strcpy is not allowed in this profile\n");
        return std::strcpy(s1, s2);
#elif defined(PROFILE_ERROR)
        static_assert(0, "strcpy is not allowed in this profile\n");
        return 0;
#else
        return std::strcpy(s1, s2);
#endif
     }

}
using namespace my_functions;

For this to work you cannot include or have using namespace std;

John Bandela
  • 2,416
  • 12
  • 19