public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* RFC: GCC plugin to find encrypted function pointer calls in glibc
@ 2016-04-29 10:23 Aldy Hernandez
  2016-04-30  1:21 ` Carlos O'Donell
  2016-05-02 22:36 ` Roland McGrath
  0 siblings, 2 replies; 16+ messages in thread
From: Aldy Hernandez @ 2016-04-29 10:23 UTC (permalink / raw)
  To: libc-alpha, Florian Weimer

[-- Attachment #1: Type: text/plain, Size: 2288 bytes --]

Hi folks!

I'm working on a GCC plugin to pinpoint places in glibc where indirect 
function calls are made through a function pointer that has not been 
demangled.

As a reference, I am attaching a test case with various things that my 
plugin currently warns on that may serve as examples to what I am trying 
to accomplish.

The general idea is that I have defined a decryption operation as the 
output of a binary operator (xor, ior, and), so while we would warn for 
something like this:

	typedef void (*callback_t) (void);
	void foo (callback_t cb)
	{
	  cb ();
	}

...the following would be OK:

	#define PTR_DEMANGLE(var) \
	  (var) = (__typeof(var)) ((unsigned long) (var) ^ MAGIC)

	typedef void (*callback_t) (void);
	void foo (callback_t cb)
	{
	  callback_t tmp = cb;
	  PTR_DEMANGLE (tmp);
	  tmp ();
	}

However, I see glibc uses the following idiom:

#  define PTR_DEMANGLE(reg)	asm ("ror $2*" LP_SIZE "+1....)

Since I would prefer not to assume the output of all inline asm's are a 
demangling operation, I would like to get feedback from the community on 
what would be preferred.

My preferred approach is to add an attribute to an inline function that 
would wrap the asm:

	__attribute__((decrypt)) static inline funcp demangler (funcp f)
	{
		asm("blah");
	}

This is straightforward, clean, and follows language semantics (not to 
mention that I already have it implemented into my plugin :)), but 
Florian made funny faces when I showed it to him, so here I am :).

It would be neat if GCC had a way of tagging individual gimple 
statements with an attribute, so we could tag them with 
__attribute__((decrypt)), but alas we don't have such mechanism, and I'd 
prefer not to perform major surgery to GCC to make it so.

Another alternative would be to tag inline asms with commented out magic 
at the end, such that the plugin would notice and take appropriate action:

#ifdef FUNCTION_POINTER_CHECKS
#define DEMANGLE_TAG " ##_DEMANGLE_##"
#else
#define DEMANGLE_TAG ""
#endif
#  define PTR_DEMANGLE(var)  asm("ror $2*..."##DEMANGLE_TAG \
				: "=r" (var)
				: "0" (var)
				etc
				etc

This is straightforward, but I still prefer the inline function plus 
attribute idea.

If anyone is interested, I can post the code to my plugin.

What do y'all think?

Aldy

[-- Attachment #2: test-funcp.c --]
[-- Type: text/plain, Size: 2947 bytes --]

typedef void (*funcp) (void);

/* Binary operations on a function pointer is a recognized form of
   pointer encryption.

   The decryption must be done in place (to a local/temporary or a
   register), and never written to memory, otherwise it is considered
   unsafe.  */
#define PTR_DEMANGLE(var) \
  (var) = (__typeof(var)) ((unsigned long) (var) ^ 0xc00ffee)

/* A function with __attribute__((encrypt)) is considered an
   acceptable way to encrypt a function pointer.  Similar to PTR_DEMANGLE
   above, the decryption must be done in place.

   Note: It would be neat if we could tag the attribute directly onto the
   asm statement, but GCC currently doesn't do this, and it would
   probably be over kill.  */
__attribute__((encrypt))
static funcp asm_demangler (funcp f)
{
  asm ("#decrypt_operation $2, $0" : "=r" (f) : "0" (f));
  return f;
}

void (*encrypted_funcp) (void);
void (*plain_funcp) (void);
void direct_function (void);

extern int bar();

void foo()
{
  PTR_DEMANGLE (encrypted_funcp);
  encrypted_funcp ();		// WARN, written to memory.

  encrypted_funcp = asm_demangler (encrypted_funcp);
  encrypted_funcp ();		// WARN, written to memory.

  plain_funcp ();		// WARN, no decryption at all.

  direct_function();		// OK.  Don't care about plain func calls.

  funcp f = encrypted_funcp;
  f = asm_demangler (f);
  f();				// OK.  Decryption to a temporary, not memory.

  f = encrypted_funcp;
  f();				// WARN, no decryption at all.

  PTR_DEMANGLE (f);
  f();				// OK. Decryption in place.

  f = encrypted_funcp;
  if (bar())
    PTR_DEMANGLE (f);
  f ();				// WARN, path not demangled.

  // Test that phi convergence code works.
  f = encrypted_funcp;
  if (bar())
    PTR_DEMANGLE (f);
  else
    {
      funcp g = f;
      PTR_DEMANGLE (g);
      f = g;
    }
  f ();				// OK, all paths demangled.

  f = encrypted_funcp;
  if (bar())
    PTR_DEMANGLE (f);
  f();				// WARN, only one path mangled.

  extern void qsort(funcp callback);
  qsort (encrypted_funcp); // WARN, passing non decrypted funcp in argument

  qsort(0);		    // OK to pass a constant even though it's
			    // not decrypted.

  if (encrypted_funcp == 0) // WARN, comparing non-decrypted with constant.
    direct_function();

  f = encrypted_funcp;
  PTR_DEMANGLE (f);
  if (f != 0)		    // OK to compare with constant, we've been
			    // demangled.
    direct_function();
}

void foo2()
{
  funcp f = encrypted_funcp;
  funcp g = plain_funcp;
  PTR_DEMANGLE (f);
  if (f == g)		   // WARN, incompatible encryptness comparison
    bar();

  f = encrypted_funcp;     // OK to compare same encryption type.
  if (f == g)
    bar();
}

// Test that the propagator doesn't go into an infinite loop.

void
bad_inifinite_loop (const signed char *inptr)
{
  while (1)
    {
      signed char ch = *inptr;

      if (!ch)
        continue;

      ch &= 0x1f;

      for (unsigned i = 1; i < 10000; ++i)
        ch |= inptr[0] & 0x3f;
    }
}

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2016-05-19 12:11 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-29 10:23 RFC: GCC plugin to find encrypted function pointer calls in glibc Aldy Hernandez
2016-04-30  1:21 ` Carlos O'Donell
2016-04-30 15:57   ` Aldy Hernandez
2016-05-02 15:26     ` Jeff Law
2016-05-02 16:26       ` Florian Weimer
2016-05-04 13:59     ` Carlos O'Donell
2016-05-05 10:10       ` Aldy Hernandez
2016-05-05 12:47         ` Mikhail Maltsev
2016-05-19 13:00           ` Aldy Hernandez
2016-04-30 16:12   ` Florian Weimer
2016-04-30 16:20     ` Aldy Hernandez
2016-04-30 17:15       ` Florian Weimer
2016-05-02 12:02   ` Aldy Hernandez
2016-05-02 15:24   ` Jeff Law
2016-05-02 22:36 ` Roland McGrath
2016-05-03  7:02   ` Aldy Hernandez

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).