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; } }