123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- /* Copyright (C) 2012-2013
- Free Software Foundation
- This file is part of GCC.
- GCC is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3, or (at your option)
- any later version.
- GCC is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- Under Section 7 of GPL version 3, you are granted additional
- permissions described in the GCC Runtime Library Exception, version
- 3.1, as published by the Free Software Foundation.
- You should have received a copy of the GNU General Public License and
- a copy of the GCC Runtime Library Exception along with this program;
- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
- <http://www.gnu.org/licenses/>. */
- /* This file is part of the vtable security feature implementation.
- The vtable security feature is designed to detect when a virtual
- call is about to be made through an invalid vtable pointer
- (possibly due to data corruption or malicious attacks).
- This file also contains the failure functions that get called when
- a vtable pointer is not found in the data set. Two particularly
- important functions are __vtv_verify_fail and __vtv_really_fail.
- They are both externally visible. __vtv_verify_fail is defined in
- such a way that it can be replaced by a programmer, if desired. It
- is the function that __VLTVerifyVtablePointer calls if it can't
- find the pointer in the data set. Allowing the programmer to
- overwrite this function means that he/she can do some alternate
- verification, including NOT failing in certain specific cases, if
- desired. This may be the case if the programmer has to deal wtih
- unverified third party software, for example. __vtv_really_fail is
- available for the programmer to call from his version of
- __vtv_verify_fail, if he decides the failure is real.
- */
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #if !defined (__CYGWIN__) && !defined (__MINGW32__)
- #include <execinfo.h>
- #endif
- #include <unistd.h>
- #include "vtv_utils.h"
- #include "vtv_fail.h"
- /* This is used to disable aborts for debugging purposes. */
- bool vtv_no_abort = false;
- extern "C" {
- /* __fortify_fail is a function in glibc that calls __libc_message,
- causing it to print out a program termination error message
- (including the name of the binary being terminated), a stack
- trace where the error occurred, and a memory map dump. Ideally
- we would have called __libc_message directly, but that function
- does not appear to be accessible to functions outside glibc,
- whereas __fortify_fail is. We call __fortify_fail from
- __vtv_really_fail. We looked at calling __libc_fatal, which is
- externally accessible, but it does not do the back trace and
- memory dump. */
- extern void __fortify_fail (const char *) __attribute__((noreturn));
- } /* extern "C" */
- const unsigned long SET_HANDLE_HANDLE_BIT = 0x2;
- /* Instantiate the template classes (in vtv_set.h) for our particular
- hash table needs. */
- typedef void * vtv_set_handle;
- typedef vtv_set_handle * vtv_set_handle_handle;
- static int vtv_failures_log_fd = -1;
- /* Open error logging file, if not already open, and write vtable
- verification failure messages (LOG_MSG) to the log file. Also
- generate a backtrace in the log file, if GENERATE_BACKTRACE is
- set. */
- static void
- log_error_message (const char *log_msg, bool generate_backtrace)
- {
- if (vtv_failures_log_fd == -1)
- vtv_failures_log_fd = vtv_open_log ("vtable_verification_failures.log");
- if (vtv_failures_log_fd == -1)
- return;
- vtv_add_to_log (vtv_failures_log_fd, "%s", log_msg);
- if (generate_backtrace)
- {
- #define STACK_DEPTH 20
- void *callers[STACK_DEPTH];
- #if !defined (__CYGWIN__) && !defined (__MINGW32__)
- int actual_depth = backtrace (callers, STACK_DEPTH);
- backtrace_symbols_fd (callers, actual_depth, vtv_failures_log_fd);
- #endif
- }
- }
- /* In the case where a vtable map variable is the only instance of the
- variable we have seen, it points directly to the set of valid
- vtable pointers. All subsequent instances of the 'same' vtable map
- variable point to the first vtable map variable. This function,
- given a vtable map variable PTR, checks a bit to see whether it's
- pointing directly to the data set or to the first vtable map
- variable. */
- static inline bool
- is_set_handle_handle (void * ptr)
- {
- return ((unsigned long) ptr & SET_HANDLE_HANDLE_BIT)
- == SET_HANDLE_HANDLE_BIT;
- }
- /* Returns the actual pointer value of a vtable map variable, PTR (see
- comments for is_set_handle_handle for more details). */
- static inline vtv_set_handle *
- ptr_from_set_handle_handle (void * ptr)
- {
- return (vtv_set_handle *) ((unsigned long) ptr & ~SET_HANDLE_HANDLE_BIT);
- }
- /* Given a vtable map variable, PTR, this function sets the bit that
- says this is the second (or later) instance of a vtable map
- variable. */
- static inline vtv_set_handle_handle
- set_handle_handle (vtv_set_handle * ptr)
- {
- return (vtv_set_handle_handle) ((unsigned long) ptr | SET_HANDLE_HANDLE_BIT);
- }
- /* This function is called from __VLTVerifyVtablePointerDebug; it
- sends as much debugging information as it can to the error log
- file, then calls __vtv_verify_fail. SET_HANDLE_PTR is the pointer
- to the set of valid vtable pointers, VTBL_PTR is the pointer that
- was not found in the set, and DEBUG_MSG is the message to be
- written to the log file before failing. n */
- void
- __vtv_verify_fail_debug (void **set_handle_ptr, const void *vtbl_ptr,
- const char *debug_msg)
- {
- log_error_message (debug_msg, false);
- /* Call the public interface in case it has been overwritten by
- user. */
- __vtv_verify_fail (set_handle_ptr, vtbl_ptr);
- log_error_message ("Returned from __vtv_verify_fail."
- " Secondary verification succeeded.\n", false);
- }
- /* This function calls __fortify_fail with a FAILURE_MSG and then
- calls abort. */
- void
- __vtv_really_fail (const char *failure_msg)
- {
- __fortify_fail (failure_msg);
- /* We should never get this far; __fortify_fail calls __libc_message
- which prints out a back trace and a memory dump and then is
- supposed to call abort, but let's play it safe anyway and call abort
- ourselves. */
- abort ();
- }
- /* This function takes an error MSG, a vtable map variable
- (DATA_SET_PTR) and a vtable pointer (VTBL_PTR). It is called when
- an attempt to verify VTBL_PTR with the set pointed to by
- DATA_SET_PTR failed. It outputs a failure message with the
- addresses involved, and calls __vtv_really_fail. */
- static void
- vtv_fail (const char *msg, void **data_set_ptr, const void *vtbl_ptr)
- {
- char buffer[128];
- int buf_len;
- const char *format_str =
- "*** Unable to verify vtable pointer (%p) in set (%p) *** \n";
- snprintf (buffer, sizeof (buffer), format_str, vtbl_ptr,
- is_set_handle_handle(*data_set_ptr) ?
- ptr_from_set_handle_handle (*data_set_ptr) :
- *data_set_ptr);
- buf_len = strlen (buffer);
- /* Send this to to stderr. */
- write (2, buffer, buf_len);
- if (!vtv_no_abort)
- __vtv_really_fail (msg);
- }
- /* Send information about what we were trying to do when verification
- failed to the error log, then call vtv_fail. This function can be
- overwritten/replaced by the user, to implement a secondary
- verification function instead. DATA_SET_PTR is the vtable map
- variable used for the failed verification, and VTBL_PTR is the
- vtable pointer that was not found in the set. */
- void
- __vtv_verify_fail (void **data_set_ptr, const void *vtbl_ptr)
- {
- char log_msg[256];
- snprintf (log_msg, sizeof (log_msg), "Looking for vtable %p in set %p.\n",
- vtbl_ptr,
- is_set_handle_handle (*data_set_ptr) ?
- ptr_from_set_handle_handle (*data_set_ptr) :
- *data_set_ptr);
- log_error_message (log_msg, false);
- const char *format_str =
- "*** Unable to verify vtable pointer (%p) in set (%p) *** \n";
- snprintf (log_msg, sizeof (log_msg), format_str, vtbl_ptr, *data_set_ptr);
- log_error_message (log_msg, false);
- log_error_message (" Backtrace: \n", true);
- const char *fail_msg = "Potential vtable pointer corruption detected!!\n";
- vtv_fail (fail_msg, data_set_ptr, vtbl_ptr);
- }
|