From aacf6238fb707b7535764b7e51e541d8fe9f19ca Mon Sep 17 00:00:00 2001 From: Adhemerval Zanella Date: Wed, 16 Apr 2025 09:32:19 -0300 Subject: [PATCH] ubsan: Add initial support for -fsanitize=undefined MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit It is enabled through a new configure flag, --enable-ubsan, and should be used for debugging and/or testing. Not all ubsan handlers are implemented, only those generated/required by glibc libraries, programs, and tests. Some extra handlers might be needed in future C++ tests, and __ubsan_handle_dynamic_type_cache_miss also needs a proper implementation. The ubsan handlers are exported from ld.so since they are used on all libraries and tests. This might interfere with ubsan from compiler runtime (when programs are built with libubsan in shared mode), and this is completely untested and/or not supported at the moment. There is no support for the UBSAN_OPTIONS environment variable, although some options are supported through glibc.ubsan tunables. Currently, glibc.ubsan.halt_on_errors can be used to avoid the process halt when any UB handler is issued. Using -fsanitize=undefined enables some extra compiler checks that are not easily enabled through the libc-diag.h macro. For instance on iconv/iconvconfig.c, gcc 14.2.1 shows: In file included from ../include/bits/string_fortified.h:1, from ../string/string.h:548, from ../include/string.h:60, from iconvconfig.c:32: In function ‘strcpy’, inlined from ‘write_output’ at iconvconfig.c:1033:7, inlined from ‘main’ at iconvconfig.c:340:14: ../string/bits/string_fortified.h:81:10: error: ‘__builtin_memcpy’ offset [0, 7] is out of the bounds [0, 0] [-Werror=array-bounds=] 81 | return __builtin___strcpy_chk (__dest, __src, __glibc_objsize (__dest)); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ../string/bits/string_fortified.h:81:10: error: ‘__builtin_memcpy’ offset [0, 7] is out of the bounds [0, 0] [-Werror=array-bounds=] cc1: all warnings being treated as errors Some extra code adjustments are required to fix such cases. This preliminary support is still incomplete: * Not all targets are supported, nor have I checked the test suitei on all successful targets. Also, I only checked with limited gcc versions (only gcc 14.2.1 and for some targets 15.0.0). Currently --enable-ubsan builds on Linux for aarch64, arm, hppa, i686, powerpc64, microblaze, mips64, loongarch64, sparc, s390x, and x86_64. * The instrumentation is disabled on rltd.c, although it is enabled on other loaders functions. * A lot of test cases show failures due to UB. Also, gcc-14 triggers an ICE building math routines. gcc-15 works correctly. --- INSTALL | 9 + Makeconfig | 6 +- config.h.in | 3 + configure | 30 ++ configure.ac | 18 + elf/Makefile | 32 +- elf/Versions | 19 + elf/dl-printf.c | 6 + elf/dl-tunables.list | 9 + elf/tst-_dl_addr_inside_object.c | 12 + elf/ubsan_error.c | 57 +++ elf/ubsan_handle_add_overflow.c | 26 ++ elf/ubsan_handle_builtin_unreachable.c | 27 ++ elf/ubsan_handle_divrem_overflow.c | 40 +++ elf/ubsan_handle_dynamic_type_cache_miss.c | 28 ++ elf/ubsan_handle_invalid_builtin.c | 39 +++ elf/ubsan_handle_load_invalid_value.c | 33 ++ elf/ubsan_handle_mul_overflow.c | 26 ++ elf/ubsan_handle_negate_overflow.c | 34 ++ elf/ubsan_handle_nonnull_arg.c | 34 ++ elf/ubsan_handle_nonnull_return_v1.c | 34 ++ elf/ubsan_handle_out_of_bounds.c | 34 ++ elf/ubsan_handle_overflow.c | 39 +++ elf/ubsan_handle_pointer_overflow.c | 62 ++++ elf/ubsan_handle_shift_out_of_bounds.c | 53 +++ elf/ubsan_handle_sub_overflow.c | 26 ++ elf/ubsan_handle_type_mismatch_v1.c | 75 ++++ elf/ubsan_handle_vla_bound_not_positive.c | 34 ++ elf/ubsan_val_to_string.c | 189 ++++++++++ elf/ubsan_vptr_type_cache.c | 21 ++ iconv/iconvconfig.c | 13 +- include/libintl.h | 3 + include/sys/cdefs.h | 6 + include/ubsan.h | 327 ++++++++++++++++++ locale/programs/locfile.h | 3 +- manual/install.texi | 8 + nss/test-netdb.c | 6 + posix/glob.c | 5 +- resolv/res_send.c | 11 +- stdio-common/tst-printf-format-s.h | 4 + stdio-common/tst-printf-format-vs.h | 4 + stdlib/stdbit.h | 9 +- sysdeps/arm/Makefile | 5 + sysdeps/generic/ldconfig.h | 3 +- sysdeps/generic/ldsodefs.h | 3 + sysdeps/generic/symbol-hacks.h | 36 ++ .../powerpc64/multiarch/stpncpy-ppc64.c | 3 +- 47 files changed, 1473 insertions(+), 31 deletions(-) create mode 100644 elf/ubsan_error.c create mode 100644 elf/ubsan_handle_add_overflow.c create mode 100644 elf/ubsan_handle_builtin_unreachable.c create mode 100644 elf/ubsan_handle_divrem_overflow.c create mode 100644 elf/ubsan_handle_dynamic_type_cache_miss.c create mode 100644 elf/ubsan_handle_invalid_builtin.c create mode 100644 elf/ubsan_handle_load_invalid_value.c create mode 100644 elf/ubsan_handle_mul_overflow.c create mode 100644 elf/ubsan_handle_negate_overflow.c create mode 100644 elf/ubsan_handle_nonnull_arg.c create mode 100644 elf/ubsan_handle_nonnull_return_v1.c create mode 100644 elf/ubsan_handle_out_of_bounds.c create mode 100644 elf/ubsan_handle_overflow.c create mode 100644 elf/ubsan_handle_pointer_overflow.c create mode 100644 elf/ubsan_handle_shift_out_of_bounds.c create mode 100644 elf/ubsan_handle_sub_overflow.c create mode 100644 elf/ubsan_handle_type_mismatch_v1.c create mode 100644 elf/ubsan_handle_vla_bound_not_positive.c create mode 100644 elf/ubsan_val_to_string.c create mode 100644 elf/ubsan_vptr_type_cache.c create mode 100644 include/ubsan.h diff --git a/INSTALL b/INSTALL index d3200f271f1..50466cbfba2 100644 --- a/INSTALL +++ b/INSTALL @@ -292,6 +292,15 @@ passed to 'configure'. For example: Default is to disable fortification. +'--enable-ubsan' + Build the GNU C library with, along with tests, with the + -fsanitize=undefined compiler option. The compiler runtime is not + used, instead UBSAN functions called by the compiler instrumentation + is provided by glibc itself. + + This is a debug/development option and the default is to disable + the instrumentation. + To build the library and related programs, type 'make'. This will produce a lot of output, some of which may look like errors from 'make' but aren't. Look for error messages from 'make' containing '***'. diff --git a/Makeconfig b/Makeconfig index a2ea4f6a332..110ae79063e 100644 --- a/Makeconfig +++ b/Makeconfig @@ -995,12 +995,14 @@ ifeq "$(strip $(+cflags))" "" +cflags := $(default_cflags) endif # $(+cflags) == "" ++ubsan-flags = $(cflags-enable-ubsan) + # Force building with -fno-common because hidden_def, compat_symbol # and other constructs do not work for common symbols (and would # otherwise require specifying __attribute__ ((nocommon)) on a # case-by-case basis). +cflags += $(cflags-cpu) $(+gccwarn) $(+merge-constants) $(+math-flags) \ - $(+stack-protector) -fno-common + $(+stack-protector) $(+ubsan-flags) -fno-common +gcc-nowarn := -w # We must filter out elf because the early bootstrap of the dynamic loader @@ -1046,7 +1048,7 @@ libio-include = -I$(..)libio built-modules = iconvprogs iconvdata ldconfig libmemusage \ libpcprofile librpcsvc locale-programs \ memusagestat nonlib nscd extramodules libnldbl libsupport \ - testsuite testsuite-internal + testsuite testsuite-internal libubsan in-module = $(subst -,_,$(firstword $(libof-$(basename $(@F))) \ $(libof-$(. */ + +#include +#include +#include +#include +#include +#include + +static void _Noreturn +ubsan_abort (void) +{ + /* abort() pulls a lot of extra definition from libc (rwlock, signal + hanlding, pthread, etc.; so use a more simpler implementation for + now. */ + raise (SIGABRT); + +#ifdef ABORT_INSTRUCTION + ABORT_INSTRUCTION; +#endif + _exit (127); +} + +void +__ubsan_error (const struct source_location *source, + const char *fmt, + ...) +{ + _dl_debug_printf_c ("UBSAN: Undefined behaviour in %s:%u:%u ", + get_source_location_file_name (source), + get_source_location_line (source), + get_source_location_column (source)); + + va_list ap; + va_start (ap, fmt); + _dl_debug_vprintf_c (fmt, ap); + va_end (ap); + + if (TUNABLE_GET (glibc, ubsan, halt_on_errors, int32_t, NULL)) + ubsan_abort (); +} diff --git a/elf/ubsan_handle_add_overflow.c b/elf/ubsan_handle_add_overflow.c new file mode 100644 index 00000000000..b2463268cf5 --- /dev/null +++ b/elf/ubsan_handle_add_overflow.c @@ -0,0 +1,26 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include "ubsan.h" + +void +__ubsan_handle_add_overflow (void *_data, void *lhs, void *rhs) +{ + __ubsan_handle_overflow (_data, lhs, rhs, "+"); +} +rtld_hidden_def (__ubsan_handle_add_overflow) diff --git a/elf/ubsan_handle_builtin_unreachable.c b/elf/ubsan_handle_builtin_unreachable.c new file mode 100644 index 00000000000..b2a7dde5c73 --- /dev/null +++ b/elf/ubsan_handle_builtin_unreachable.c @@ -0,0 +1,27 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include "ubsan.h" + +void +__ubsan_handle_builtin_unreachable (void *_data) +{ + struct unreachable_data *data = _data; + __ubsan_error (&data->location, "calling __builtin_unreachable()\n"); +} +rtld_hidden_def (__ubsan_handle_builtin_unreachable) diff --git a/elf/ubsan_handle_divrem_overflow.c b/elf/ubsan_handle_divrem_overflow.c new file mode 100644 index 00000000000..bf5788c2fd7 --- /dev/null +++ b/elf/ubsan_handle_divrem_overflow.c @@ -0,0 +1,40 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include "ubsan.h" + +void +__ubsan_handle_divrem_overflow (void *_data, void *lhs, void *rhs) +{ + struct overflow_data *data = _data; + char lhs_str[UBSAN_VAL_STR_LEN]; + char rhs_str[UBSAN_VAL_STR_LEN]; + + __ubsan_val_to_string (lhs_str, data->type, lhs); + __ubsan_val_to_string (rhs_str, data->type, rhs); + + if (ubsan_type_is_signed (data->type) + && ubsan_get_signed_val (data->type, rhs)) + __ubsan_error (&data->location, + "division overflow: division of %s by -1 cannot be " + "represented in type %s\n", + rhs_str, data->type->type_name); + else + __ubsan_error (&data->location, "division by zero"); +} +rtld_hidden_def (__ubsan_handle_divrem_overflow) diff --git a/elf/ubsan_handle_dynamic_type_cache_miss.c b/elf/ubsan_handle_dynamic_type_cache_miss.c new file mode 100644 index 00000000000..b5be609a0df --- /dev/null +++ b/elf/ubsan_handle_dynamic_type_cache_miss.c @@ -0,0 +1,28 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + + +void +__ubsan_handle_dynamic_type_cache_miss (void *_data, void *pointer, void *hash) +{ + /* TODO: this failure requires additional check to check for real + issues. Ignore for now. */ +} +rtld_hidden_def (__ubsan_handle_dynamic_type_cache_miss) diff --git a/elf/ubsan_handle_invalid_builtin.c b/elf/ubsan_handle_invalid_builtin.c new file mode 100644 index 00000000000..4518408ab1f --- /dev/null +++ b/elf/ubsan_handle_invalid_builtin.c @@ -0,0 +1,39 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +__ubsan_handle_invalid_builtin (void *_data) +{ + struct invalid_builtin_data *data = _data; + switch (data->kind) + { + case ubsan_builtin_check_kind_assume_passed_false: + __ubsan_error (&data->location, + "assumption is violated during execution\n"); + break; + default: + __ubsan_error (&data->location, + "passing zero to __builtin_%s()\n", + data->kind == ubsan_builtin_check_kind_ctz_passed_zero + ? "ctz" : "clz"); + break; + } +} +rtld_hidden_def (__ubsan_handle_invalid_builtin) diff --git a/elf/ubsan_handle_load_invalid_value.c b/elf/ubsan_handle_load_invalid_value.c new file mode 100644 index 00000000000..b2b2e9b3cd5 --- /dev/null +++ b/elf/ubsan_handle_load_invalid_value.c @@ -0,0 +1,33 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include "ubsan.h" + +void +__ubsan_handle_load_invalid_value (void *_data, void *val) +{ + struct invalid_value_data *data = _data; + char val_str[UBSAN_VAL_STR_LEN]; + + __ubsan_val_to_string (val_str, data->type, val); + __ubsan_error (&data->location, + "load of value %s is not a valid value for type %s\n", + val_str, + data->type->type_name); +} +rtld_hidden_def (__ubsan_handle_load_invalid_value) diff --git a/elf/ubsan_handle_mul_overflow.c b/elf/ubsan_handle_mul_overflow.c new file mode 100644 index 00000000000..b0010b93298 --- /dev/null +++ b/elf/ubsan_handle_mul_overflow.c @@ -0,0 +1,26 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include "ubsan.h" + +void +__ubsan_handle_mul_overflow (void *_data, void *lhs, void *rhs) +{ + __ubsan_handle_overflow (_data, lhs, rhs, "*"); +} +rtld_hidden_def (__ubsan_handle_mul_overflow) diff --git a/elf/ubsan_handle_negate_overflow.c b/elf/ubsan_handle_negate_overflow.c new file mode 100644 index 00000000000..bb0c12ea5ec --- /dev/null +++ b/elf/ubsan_handle_negate_overflow.c @@ -0,0 +1,34 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include "ubsan.h" + +void +__ubsan_handle_negate_overflow (void *_data, void *val) +{ + struct overflow_data *data = _data; + char val_str[UBSAN_VAL_STR_LEN]; + + __ubsan_val_to_string(val_str, data->type, val); + + __ubsan_error (&data->location, + "negation of %s cannot be represented in type %s\n", + val_str, + data->type->type_name); +} +rtld_hidden_def (__ubsan_handle_negate_overflow) diff --git a/elf/ubsan_handle_nonnull_arg.c b/elf/ubsan_handle_nonnull_arg.c new file mode 100644 index 00000000000..a5104fd49d1 --- /dev/null +++ b/elf/ubsan_handle_nonnull_arg.c @@ -0,0 +1,34 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include "ubsan.h" + +void +__ubsan_handle_nonnull_arg (void *_data) +{ + struct nonnull_arg_data *data = _data; + + __ubsan_error (&data->location, + "null pointer passed as argument %u, nonnull attribute " + "declared at %s:%u:%u\n", + data->arg_index, + get_source_location_file_name (&data->attr_location), + get_source_location_line (&data->attr_location), + get_source_location_column (&data->attr_location)); +} +rtld_hidden_def (__ubsan_handle_nonnull_arg) diff --git a/elf/ubsan_handle_nonnull_return_v1.c b/elf/ubsan_handle_nonnull_return_v1.c new file mode 100644 index 00000000000..a6e0d73fefe --- /dev/null +++ b/elf/ubsan_handle_nonnull_return_v1.c @@ -0,0 +1,34 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +__ubsan_handle_nonnull_return_v1 (void *_data, void *_location) +{ + struct nonnull_return_data *data = _data; + struct source_location *location = _location; + + __ubsan_error (&data->location, + "null pointer returned from function declared as " + "returns_nonnull: source %s:%u:%u\n", + get_source_location_file_name (location), + get_source_location_line (location), + get_source_location_column (location)); +} +rtld_hidden_def (__ubsan_handle_nonnull_return_v1) diff --git a/elf/ubsan_handle_out_of_bounds.c b/elf/ubsan_handle_out_of_bounds.c new file mode 100644 index 00000000000..469682ddfdf --- /dev/null +++ b/elf/ubsan_handle_out_of_bounds.c @@ -0,0 +1,34 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include "ubsan.h" + +void +__ubsan_handle_out_of_bounds (void *_data, void *index) +{ + struct out_of_bounds_data *data = _data; + char index_str[UBSAN_VAL_STR_LEN]; + + __ubsan_val_to_string (index_str, data->index_type, index); + + __ubsan_error (&data->location, + "index %s is out of bounds for type %s\n", + index_str, + data->array_type->type_name); +} +rtld_hidden_def (__ubsan_handle_out_of_bounds) diff --git a/elf/ubsan_handle_overflow.c b/elf/ubsan_handle_overflow.c new file mode 100644 index 00000000000..865ad76e49d --- /dev/null +++ b/elf/ubsan_handle_overflow.c @@ -0,0 +1,39 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include "ubsan.h" + +void +__ubsan_handle_overflow (const struct overflow_data * data, void *lhs, + void *rhs, const char *op) +{ + char lhs_str[UBSAN_VAL_STR_LEN]; + char rhs_str[UBSAN_VAL_STR_LEN]; + + __ubsan_val_to_string (lhs_str, data->type, lhs); + __ubsan_val_to_string (rhs_str, data->type, rhs); + + __ubsan_error (&data->location, + "%s integer overflow: %s %s %s cannot be represened in " + "type %s\n", + ubsan_type_is_signed (data->type) ? "signed" : "unsigned", + lhs_str, + op, + rhs_str, + data->type->type_name); +} diff --git a/elf/ubsan_handle_pointer_overflow.c b/elf/ubsan_handle_pointer_overflow.c new file mode 100644 index 00000000000..8d0c4db739e --- /dev/null +++ b/elf/ubsan_handle_pointer_overflow.c @@ -0,0 +1,62 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +void +__ubsan_handle_pointer_overflow (void *_data, void *val, void *result) +{ + struct pointer_overflow_data *data = _data; + + if (val == NULL && result == NULL) + __ubsan_error (&data->location, + "applying zero offset to a NULL pointer\n"); + else if (val == NULL && result != NULL) + __ubsan_error (&data->location, + "applying non-zero offset to a NULL pointer\n"); + else if (val != NULL && result == NULL) + __ubsan_error (&data->location, + "applying non-zero offset to non-NULL pointer 0x%0*lx " + "produced NULL pointer\n", + (int) sizeof (void *) * 2, + (unsigned long int) val); + else if (((intptr_t)val >= 0) == ((intptr_t)result >= 0)) + { + const char *operation = ((uintptr_t)val > (uintptr_t)result) + ? "addition" : "subtraction"; + + __ubsan_error (&data->location, + "%s of unsigned offset to 0x%0*lx overflowed " + "to 0x%0*lx\n", + operation, + (int) sizeof (void *) * 2, + (unsigned long int) val, + (int) sizeof (void *) * 2, + (unsigned long int) result); + } + else + __ubsan_error (&data->location, + "pointer index expression with base 0x%0*lx overflowed " + "to 0x%0*lx\n", + (int) sizeof (void *) * 2, + (unsigned long int) val, + (int) sizeof (void *) * 2, + (unsigned long int) result); +} +rtld_hidden_def (__ubsan_handle_pointer_overflow) diff --git a/elf/ubsan_handle_shift_out_of_bounds.c b/elf/ubsan_handle_shift_out_of_bounds.c new file mode 100644 index 00000000000..f8402778baf --- /dev/null +++ b/elf/ubsan_handle_shift_out_of_bounds.c @@ -0,0 +1,53 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +__ubsan_handle_shift_out_of_bounds (void *_data, void *lhs, void *rhs) +{ + struct shift_out_of_bounds_data *data = _data; + char lhs_str[UBSAN_VAL_STR_LEN]; + char rhs_str[UBSAN_VAL_STR_LEN]; + + __ubsan_val_to_string (lhs_str, data->lhs_type, lhs); + __ubsan_val_to_string (rhs_str, data->rhs_type, rhs); + + if (ubsan_val_is_negative (data->rhs_type, rhs)) + __ubsan_error (&data->location, + "shift expoenent %s is negative\n", + rhs_str); + else if (ubsan_get_unsigned_val (data->rhs_type, rhs) >= + ubsan_type_bit_width (data->lhs_type)) + __ubsan_error (&data->location, + "shift exponent %s is too large for %u-bit type %s\n", + rhs_str, + ubsan_type_bit_width (data->lhs_type), + data->lhs_type->type_name); + else if (ubsan_val_is_negative (data->lhs_type, lhs)) + __ubsan_error (&data->location, + "left shift of negative valor %s\n", + lhs_str); + else + __ubsan_error (&data->location, + "left shift of %s by %s cannot be represented in type %s\n", + lhs_str, + rhs_str, + data->lhs_type->type_name); +} +rtld_hidden_def (__ubsan_handle_shift_out_of_bounds) diff --git a/elf/ubsan_handle_sub_overflow.c b/elf/ubsan_handle_sub_overflow.c new file mode 100644 index 00000000000..8b576dae14e --- /dev/null +++ b/elf/ubsan_handle_sub_overflow.c @@ -0,0 +1,26 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +__ubsan_handle_sub_overflow (void *_data, void *lhs, void *rhs) +{ + __ubsan_handle_overflow (_data, lhs, rhs, "-"); +} +rtld_hidden_def (__ubsan_handle_sub_overflow) diff --git a/elf/ubsan_handle_type_mismatch_v1.c b/elf/ubsan_handle_type_mismatch_v1.c new file mode 100644 index 00000000000..de459b99f4a --- /dev/null +++ b/elf/ubsan_handle_type_mismatch_v1.c @@ -0,0 +1,75 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +static const char * +type_check_to_string (unsigned char type_check_kind) +{ + switch (type_check_kind) + { + case ubsan_type_check_load: return "load of"; + case ubsan_type_check_store: return "store to"; + case ubsan_type_check_reference_binding: return "reference binding to"; + case ubsan_type_check_member_access: return "member access within"; + case ubsan_type_check_member_call: return "member call on"; + case ubsan_type_check_constructor_call: return "constructor call on"; + case ubsan_type_check_downcast_pointer: + case ubsan_type_check_downcast_reference: return "downcast of"; + case ubsan_type_check_upcast: return "upcast of"; + case ubsan_type_check_upcast_to_virtual_base: return "cast to virtual base of"; + case ubsan_type_check_nonnull_assign: return "_Nonnull binding to"; + case ubsan_type_check_dynamic_operation: return "dynamic operation on"; + default: return "unknown"; + } +} + +static inline bool +is_misaligned_pointer (const struct type_mismatch_data_v1 *data, void *ptr) +{ + uintptr_t alignment = 1UL << data->log_alignment; + return (uintptr_t) ptr & (alignment - 1); +} + +void +__ubsan_handle_type_mismatch_v1 (void *_data, void *ptr) +{ + struct type_mismatch_data_v1 *data = _data; + + if (data->type_check_kind == ubsan_type_check_nonnull_assign) + __ubsan_error (&data->location, + "%s null pointer of type %s\n", + type_check_to_string (data->type_check_kind), + data->type->type_name); + else if (is_misaligned_pointer (data, ptr)) + __ubsan_error (&data->location, + "%s misaligned address 0x%0*lx for type %s\n", + type_check_to_string (data->type_check_kind), + (int) sizeof (void *) * 2, + (unsigned long int) ptr, + data->type->type_name); + else + __ubsan_error (&data->location, + "%s address 0x%0*lx with insufficient space for an " + "object of type %s\n", + type_check_to_string (data->type_check_kind), + (int) sizeof (void *) * 2, + (unsigned long int) ptr, + data->type->type_name); +} +rtld_hidden_def (__ubsan_handle_type_mismatch_v1) diff --git a/elf/ubsan_handle_vla_bound_not_positive.c b/elf/ubsan_handle_vla_bound_not_positive.c new file mode 100644 index 00000000000..e2b3f5232d6 --- /dev/null +++ b/elf/ubsan_handle_vla_bound_not_positive.c @@ -0,0 +1,34 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +__ubsan_handle_vla_bound_not_positive (void *_data, void *bound) +{ + struct vla_bound_not_positive_data *data = _data; + char bound_str[UBSAN_VAL_STR_LEN]; + + __ubsan_val_to_string (bound_str, data->type, bound); + + __ubsan_error (&data->location, + "variable length array bound evaluates to " + "non-positive value %s\n", + bound_str); +} +rtld_hidden_def (__ubsan_handle_vla_bound_not_positive) diff --git a/elf/ubsan_val_to_string.c b/elf/ubsan_val_to_string.c new file mode 100644 index 00000000000..f354eed49d6 --- /dev/null +++ b/elf/ubsan_val_to_string.c @@ -0,0 +1,189 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +#include "ubsan.h" + +static const char lower_digits[] = "0123456789"; + +enum { BASE = 10 }; + +static char * +utoa (unsigned long long int value, char *buf, size_t len) +{ + if (len == 0) + return buf; + + char *ptr = buf; + do + { + if (len-- == 0) + break; + *ptr++ = lower_digits[value % BASE]; + value /= BASE; + } + while (value != 0); + char *r = ptr; + *ptr-- = '\0'; + + while (buf < ptr) + { + char t = *ptr; + *ptr-- = *buf; + *buf++ = t; + } + + return r; +} + +static char * +itoa (long long int value, char *buf, size_t len) +{ + if (len == 0) + return buf; + + bool isneg = value < 0; + char *ptr = buf; + do + { + if (len-- == 0) + break; + *ptr++ = lower_digits[abs (value % BASE)]; + value /= BASE; + } + while (value != 0); + if (isneg) + *ptr++ = '-'; + char *r = ptr; + *ptr-- = '\0'; + + while (buf < ptr) + { + char t = *ptr; + *ptr-- = *buf; + *buf++ = t; + } + + return r; +} + +static long long int +ubsan_val_to_ll (int width, void *value, long long int def) +{ + switch (width) + { + case 8: + return (int8_t) (intptr_t) value; + case 16: + return (int16_t) (intptr_t) value; + case 32: + if (sizeof (value) >= sizeof (int32_t)) + return (int32_t) (intptr_t) value; + else + return *(int32_t *) value; + case 64: + if (sizeof (value) >= sizeof (int64_t)) + return (int64_t) (intptr_t) value; + else + return *(int64_t *) value; + default: + return def; + } +} + +static unsigned long long int +ubsan_val_to_ull (int width, void *value, unsigned long long int def) +{ + switch (width) + { + case 8: + return (uint8_t) (uintptr_t) value; + case 16: + return (uint16_t) (uintptr_t) value; + case 32: + if (sizeof (value) >= sizeof (uint32_t)) + return (uint32_t) (uintptr_t) value; + else + return *(uint32_t *) value; + case 64: + if (sizeof (value) >= sizeof (uint64_t)) + return (uint64_t) (uintptr_t) value; + else + return *(uint64_t *) value; + default: + return def; + } +} + +static inline char * +add_string (char *str, const char *s, size_t *len) +{ + char *endp = __stpncpy (str, s, *len); + *len -= endp - str; + return endp; +} + +static inline char * +add_uint (char *str, unsigned long long int value, size_t *len) +{ + char *endp = utoa (value, str, *len); + *len -= endp - str; + return endp; +} + +void +__ubsan_val_to_string (char str[static UBSAN_VAL_STR_LEN], + struct type_descriptor *type, void *value) +{ + int width = ubsan_type_bit_width (type); + switch (type->type_kind) + { + case ubsan_type_kind_int: + if (ubsan_type_is_signed (type)) + { + long long int v = ubsan_val_to_ll (width, value, 0); + itoa (v, str, UBSAN_VAL_STR_LEN); + } + else + { + unsigned long long int v = ubsan_val_to_ull (width, value, 0); + utoa (v, str, UBSAN_VAL_STR_LEN); + } + break; + case ubsan_type_kind_float: + { + char *endp = __stpcpy (str, "float size "); + utoa (width, endp, UBSAN_VAL_STR_LEN); + } + break; + default: + { + size_t size = UBSAN_VAL_STR_LEN; + char *endp = add_string (str, "kind ", &size); + endp = add_uint (endp, type->type_kind, &size); + endp = add_string (endp, " (width ", &size); + endp = add_uint (endp, width, &size); + add_string (endp, " )", &size); + } + break; + } +} diff --git a/elf/ubsan_vptr_type_cache.c b/elf/ubsan_vptr_type_cache.c new file mode 100644 index 00000000000..77572b2487e --- /dev/null +++ b/elf/ubsan_vptr_type_cache.c @@ -0,0 +1,21 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +unsigned int __ubsan_vptr_type_cache[UBSAN_VPTR_TYPE_CACHE_SIZE]; diff --git a/iconv/iconvconfig.c b/iconv/iconvconfig.c index aa6f381266b..8975aa086e8 100644 --- a/iconv/iconvconfig.c +++ b/iconv/iconvconfig.c @@ -1019,11 +1019,14 @@ write_output (void) /* Open the output file. */ if (output_file == NULL) { - assert (GCONV_MODULES_CACHE[0] == '/'); - strcpy (stpcpy (mempcpy (tmpfname, prefix, prefix_len), - GCONV_MODULES_CACHE), - ".XXXXXX"); - strcpy (mempcpy (finalname, prefix, prefix_len), GCONV_MODULES_CACHE); + snprintf (tmpfname, sizeof tmpfname, "%.*s%s.XXXXXX", + (int) prefix_len, + prefix, + GCONV_MODULES_CACHE); + snprintf (finalname, sizeof finalname, "%.*s%s", + (int) prefix_len, + prefix, + GCONV_MODULES_CACHE); } else strcpy (mempcpy (tmpfname, output_file, output_file_len), ".XXXXXX"); diff --git a/include/libintl.h b/include/libintl.h index 3d63b7abbd3..57039f1fc13 100644 --- a/include/libintl.h +++ b/include/libintl.h @@ -65,5 +65,8 @@ libc_hidden_proto (_libc_intl_domainname) # undef N_ # define N_(msgid) msgid +# undef gettext +# define gettext(msgid) (dgettext (NULL, msgid) ?: (char *)msgid) + # endif /* !_ISOMAC */ #endif diff --git a/include/sys/cdefs.h b/include/sys/cdefs.h index a676f75f625..8ec2079fa4d 100644 --- a/include/sys/cdefs.h +++ b/include/sys/cdefs.h @@ -63,4 +63,10 @@ rtld_hidden_proto (__chk_fail) # define __attribute_optimization_barrier__ __attribute__ ((noinline, noclone)) #endif +#ifdef ENABLE_UBSAN +# define __attribute_disable_ubsan__ __attribute__((no_sanitize("undefined"))) +#else +# define __attribute_disable_ubsan__ +#endif + #endif diff --git a/include/ubsan.h b/include/ubsan.h new file mode 100644 index 00000000000..56d87215071 --- /dev/null +++ b/include/ubsan.h @@ -0,0 +1,327 @@ +/* Undefined Behavior Sanitizer support. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef __UBSAN_H__ +#define __UBSAN_H__ + +#include +#include +#include + +#ifdef __SIZEOF_INT128__ +typedef __int128 ubsan_s_max; +typedef unsigned __int128 ubsan_u_max; +#else +typedef int64_t ubsan_u_max; +typedef uint64_t ubsan_s_max; +#endif + +#define REPORTED_BIT 31 +#if (__WORDSIZE == 64 && BYTE_ORDER == BIG_ENDIAN) +# define COLUMN_MASK (~(1U << REPORTED_BIT)) +# define LINE_MASK (~0U) +#else +# define COLUMN_MASK (~0U) +# define LINE_MASK (~(1U << REPORTED_BIT)) +#endif + +struct source_location +{ + const char *file_name; + unsigned int line; + unsigned int column; +}; + +static inline const char * +get_source_location_file_name (const struct source_location *location) +{ + return location->file_name ? location->file_name : "unknown"; +} + +static inline unsigned int +get_source_location_line (const struct source_location *location) +{ + return location->line & LINE_MASK; +} + +static inline unsigned int +get_source_location_column (const struct source_location *location) +{ + return location->column & COLUMN_MASK; +} + +struct nonnull_arg_data +{ + struct source_location location; + struct source_location attr_location; + int arg_index; +}; + +struct type_descriptor +{ + uint16_t type_kind; + uint16_t type_info; + char type_name[]; +}; + +static inline bool +ubsan_type_is_signed (const struct type_descriptor *type) +{ + return type->type_kind & 1; +} + +static inline unsigned int +ubsan_type_bit_width (const struct type_descriptor *type) +{ + return 1 << (type->type_info >> 1); +} + +static inline bool +ubsan_is_inline_int (const struct type_descriptor *type) +{ + unsigned int inline_bits = sizeof (unsigned long) * 8; + unsigned int bits = ubsan_type_bit_width (type); + + return bits <= inline_bits; +} + +static inline ubsan_s_max +ubsan_get_signed_val (const struct type_descriptor *type, void *val) +{ + if (ubsan_is_inline_int (type)) + { + unsigned int extra_bits = sizeof (ubsan_s_max) * 8 + - ubsan_type_bit_width (type); + unsigned long ul_val = (unsigned long) val; + return ((ubsan_s_max) ul_val) << extra_bits >> extra_bits; + } + + if (ubsan_type_bit_width (type) == 64) + return *(int64_t*) val; + + return *(ubsan_s_max *) val; +} + +static inline ubsan_u_max +ubsan_get_unsigned_val (const struct type_descriptor *type, void *val) +{ + if (ubsan_is_inline_int (type)) + return (unsigned long) val; + + if (ubsan_type_bit_width (type) == 64) + return *(uint64_t*) val; + + return *(ubsan_u_max *)val; +} + +static inline bool +ubsan_val_is_negative (const struct type_descriptor *type, void *val) +{ + return ubsan_type_is_signed (type) && ubsan_get_signed_val (type, val) < 0; +} + +struct invalid_value_data +{ + struct source_location location; + struct type_descriptor *type; +}; + +/* The type_mismatch_data_v1::type_check_kind */ +enum +{ + ubsan_type_check_load, + ubsan_type_check_store, + ubsan_type_check_reference_binding, + ubsan_type_check_member_access, + ubsan_type_check_member_call, + ubsan_type_check_constructor_call, + ubsan_type_check_downcast_pointer, + ubsan_type_check_downcast_reference, + ubsan_type_check_upcast, + ubsan_type_check_upcast_to_virtual_base, + ubsan_type_check_nonnull_assign, + ubsan_type_check_dynamic_operation +}; + +struct type_mismatch_data_v1 +{ + struct source_location location; + struct type_descriptor *type; + unsigned char log_alignment; + unsigned char type_check_kind; +}; + +struct pointer_overflow_data +{ + struct source_location location; +}; + +struct overflow_data +{ + struct source_location location; + struct type_descriptor *type; +}; + +struct out_of_bounds_data +{ + struct source_location location; + struct type_descriptor *array_type; + struct type_descriptor *index_type; +}; + +struct shift_out_of_bounds_data +{ + struct source_location location; + struct type_descriptor *lhs_type; + struct type_descriptor *rhs_type; +}; + +struct vla_bound_not_positive_data +{ + struct source_location location; + struct type_descriptor *type; +}; + +struct unreachable_data +{ + struct source_location location; +}; + +struct invalid_builtin_data +{ + struct source_location location; + unsigned char kind; +}; + +struct nonnull_return_data +{ + struct source_location location; +}; + +struct dynamic_type_cache_miss_data +{ + struct source_location location; + struct type_descriptor *type; + void *info; + unsigned char kind; +}; + +enum +{ + ubsan_type_kind_int = 0, + ubsan_type_kind_float = 1, + ubsan_type_unknown = 0xffff +}; + +enum +{ + ubsan_builtin_check_kind_ctz_passed_zero, + ubsan_builtin_check_kind_clz_passed_zero, + ubsan_builtin_check_kind_assume_passed_false, +}; + +#define UBSAN_VAL_STR_LEN 32 + +void +__ubsan_val_to_string (char str[static UBSAN_VAL_STR_LEN], + struct type_descriptor *type, void *value) + attribute_hidden; + +#define UBSAN_VPTR_TYPE_CACHE_SIZE 128 + +extern unsigned int __ubsan_vptr_type_cache[UBSAN_VPTR_TYPE_CACHE_SIZE]; + +#if IS_IN(rtld) +# define ubsan_hidden attribute_hidden +#else +# define ubsan_hidden +#endif + +void __ubsan_error (const struct source_location *source, + const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))) + attribute_hidden; + +void __ubsan_handle_overflow (const struct overflow_data *, void *, + void *, const char *op) + attribute_hidden; + +void __ubsan_handle_load_invalid_value (void *data, void *value) + ubsan_hidden; +rtld_hidden_proto (__ubsan_handle_load_invalid_value) + +void __ubsan_handle_type_mismatch_v1 (void *data, void *ptr) + ubsan_hidden; +rtld_hidden_proto (__ubsan_handle_type_mismatch_v1) + +void __ubsan_handle_pointer_overflow (void *data, void *val, void *result) + ubsan_hidden; +rtld_hidden_proto (__ubsan_handle_pointer_overflow) + +void __ubsan_handle_add_overflow (void *data, void *lhs, void *rhs) + ubsan_hidden; +rtld_hidden_proto (__ubsan_handle_add_overflow) + +void __ubsan_handle_sub_overflow (void *data, void *lhs, void *rhs) + ubsan_hidden; +rtld_hidden_proto (__ubsan_handle_sub_overflow) + +void __ubsan_handle_mul_overflow (void *data, void *lhs, void *rhs) + attribute_hidden; +rtld_hidden_proto (__ubsan_handle_mul_overflow) + +void __ubsan_handle_out_of_bounds (void *data, void *index) + ubsan_hidden; +rtld_hidden_proto (__ubsan_handle_out_of_bounds) + +void __ubsan_handle_negate_overflow (void *data, void *val) + ubsan_hidden; +rtld_hidden_proto (__ubsan_handle_negate_overflow) + +void __ubsan_handle_shift_out_of_bounds (void *_data, void *lhs, void *rhs) + ubsan_hidden; +rtld_hidden_proto (__ubsan_handle_shift_out_of_bounds) + +void __ubsan_handle_divrem_overflow (void *_data, void *lhs, void *rhs) + ubsan_hidden; +rtld_hidden_proto (__ubsan_handle_divrem_overflow) + +void __ubsan_handle_vla_bound_not_positive (void *data, void *bound) + ubsan_hidden; +rtld_hidden_proto (__ubsan_handle_vla_bound_not_positive) + +void __ubsan_handle_builtin_unreachable (void *data) + ubsan_hidden; +rtld_hidden_proto (__ubsan_handle_builtin_unreachable) + +void __ubsan_handle_invalid_builtin (void *data) + ubsan_hidden; +rtld_hidden_proto (__ubsan_handle_invalid_builtin) + +void __ubsan_handle_nonnull_arg (void *data) + ubsan_hidden; +rtld_hidden_proto (__ubsan_handle_nonnull_arg); + +void __ubsan_handle_nonnull_return_v1 (void *data, void *location) + ubsan_hidden; +rtld_hidden_proto (__ubsan_handle_nonnull_return_v1) + +void __ubsan_handle_dynamic_type_cache_miss (void *, void *, void *); +rtld_hidden_proto (__ubsan_handle_dynamic_type_cache_miss) + +#endif /* __UBSAN_H__ */ diff --git a/locale/programs/locfile.h b/locale/programs/locfile.h index 9103fade14e..e0000bc3922 100644 --- a/locale/programs/locfile.h +++ b/locale/programs/locfile.h @@ -124,7 +124,8 @@ extern void end_locale_structure (struct locale_file *file); extern void start_locale_prelude (struct locale_file *file); extern void end_locale_prelude (struct locale_file *file); extern void write_locale_data (const char *output_path, int catidx, - const char *category, struct locale_file *file); + const char *category, struct locale_file *file) + __attribute__ ((nonnull (1, 3, 4))); /* Entrypoints for the parsers of the individual categories. */ diff --git a/manual/install.texi b/manual/install.texi index 7fcdda9146b..1e4242dff4f 100644 --- a/manual/install.texi +++ b/manual/install.texi @@ -320,6 +320,14 @@ If not provided, @option{LEVEL} defaults to highest possible value supported by the build compiler. Default is to disable fortification. + +@item --enable-ubsan +Build @theglibc{}, along with tests, with the @code{-fsanitize=undefined} +compiler option. The compiler runtime is not used, instead UBSAN functions +called by the compiler instrumentation is provided by glibc itself. + +This is a debug/development option and the default is to disable +the instrumentation. @end table To build the library and related programs, type @code{make}. This will diff --git a/nss/test-netdb.c b/nss/test-netdb.c index 3fb9ba0a427..3911c3c42aa 100644 --- a/nss/test-netdb.c +++ b/nss/test-netdb.c @@ -41,6 +41,9 @@ #include +#define assume(R) ((R) ? (void) 0 : __builtin_unreachable ()) +#define assume_nonnull(x) assume ((x) != NULL) + /* The following define is necessary for glibc 2.0.6 */ @@ -180,6 +183,9 @@ test_hosts (void) namelen += 2; /* tiny increments to test a lot */ name = xrealloc (name, namelen); } + + assume_nonnull (name); + if (gethostname (name, namelen) == 0) { printf ("Hostname: %s\n", name); diff --git a/posix/glob.c b/posix/glob.c index a7c7dd1ebe1..60ca0147ac9 100644 --- a/posix/glob.c +++ b/posix/glob.c @@ -1397,9 +1397,10 @@ glob_in_dir (const char *pattern, const char *directory, int flags, if (s.length < need && !scratch_buffer_set_array_size (&s, need, 1)) goto memory_error; - char *p = mempcpy (s.data, directory, dirlen); + char *pdata = s.data; + char *p = mempcpy (pdata, directory, dirlen); *p = '/'; - p += p[-1] != '/'; + p += pdata[p - pdata - 1] != '/'; memcpy (p, d.name, namelen + 1); if (! is_dir (s.data, flags, pglob)) continue; diff --git a/resolv/res_send.c b/resolv/res_send.c index 802675995d8..37444fc925c 100644 --- a/resolv/res_send.c +++ b/resolv/res_send.c @@ -801,7 +801,7 @@ reopen (res_state statp, int *terrno, int ns) { if (EXT(statp).nssocks[ns] == -1) { struct sockaddr *nsap = __res_get_nsaddr (statp, ns); - socklen_t slen; + socklen_t slen = 0; /* only try IPv6 if IPv6 NS and if not failed before */ if (nsap->sa_family == AF_INET6 && !statp->ipv6_unavail) { @@ -845,16 +845,7 @@ reopen (res_state statp, int *terrno, int ns) * error message is received. We can thus detect * the absence of a nameserver without timing out. */ - /* With GCC 5.3 when compiling with -Os the compiler - emits a warning that slen may be used uninitialized, - but that is never true. Both slen and - EXT(statp).nssocks[ns] are initialized together or - the function return -1 before control flow reaches - the call to connect with slen. */ - DIAG_PUSH_NEEDS_COMMENT; - DIAG_IGNORE_Os_NEEDS_COMMENT (5, "-Wmaybe-uninitialized"); if (__connect (EXT (statp).nssocks[ns], nsap, slen) < 0) { - DIAG_POP_NEEDS_COMMENT; __res_iclose(statp, false); return (0); } diff --git a/stdio-common/tst-printf-format-s.h b/stdio-common/tst-printf-format-s.h index 20369b8e860..92946cbcbae 100644 --- a/stdio-common/tst-printf-format-s.h +++ b/stdio-common/tst-printf-format-s.h @@ -21,6 +21,9 @@ #include +#define assume(R) ((R) ? (void) 0 : __builtin_unreachable ()) +#define assume_nonnull(x) assume ((x) != NULL) + #define SPRINTF_BUFFER_SIZE 65536 static struct support_next_to_fault ntf; @@ -42,6 +45,7 @@ printf_under_test_fini (void) ({ \ __label__ out; \ char *str = ntf.buffer; \ + assume_nonnull (str); \ int result; \ \ result = sprintf (str, __VA_ARGS__); \ diff --git a/stdio-common/tst-printf-format-vs.h b/stdio-common/tst-printf-format-vs.h index f99747a9248..15d5341f808 100644 --- a/stdio-common/tst-printf-format-vs.h +++ b/stdio-common/tst-printf-format-vs.h @@ -22,6 +22,9 @@ #include +#define assume(R) ((R) ? (void) 0 : __builtin_unreachable ()) +#define assume_nonnull(x) assume ((x) != NULL) + #define SPRINTF_BUFFER_SIZE 65536 static struct support_next_to_fault ntf; @@ -43,6 +46,7 @@ static int printf_under_test (const char *restrict fmt, ...) { char *str = ntf.buffer; + assume_nonnull (str); va_list ap; int result; diff --git a/stdlib/stdbit.h b/stdlib/stdbit.h index 4afa4362d11..36b92901e5b 100644 --- a/stdlib/stdbit.h +++ b/stdlib/stdbit.h @@ -44,13 +44,8 @@ __BEGIN_DECLS /* Use __pacify_uint16 (N) instead of (uint16_t) (N) when the cast is helpful only to pacify older GCC (e.g., GCC 10 -Wconversion) or non-GCC (e.g clang -Wimplicit-int-conversion). */ -#if __GNUC_PREREQ (11, 0) -# define __pacify_uint8(n) (n) -# define __pacify_uint16(n) (n) -#else -# define __pacify_uint8(n) ((uint8_t) (n)) -# define __pacify_uint16(n) ((uint16_t) (n)) -#endif +#define __pacify_uint8(n) ((uint8_t) (n)) +#define __pacify_uint16(n) ((uint16_t) (n)) /* Count leading zeros. */ extern unsigned int stdc_leading_zeros_uc (unsigned char __x) diff --git a/sysdeps/arm/Makefile b/sysdeps/arm/Makefile index 9c4fd6b2369..c7e92da0533 100644 --- a/sysdeps/arm/Makefile +++ b/sysdeps/arm/Makefile @@ -31,6 +31,11 @@ $(objpfx)tst-armtlsdescextnow: $(objpfx)tst-armtlsdescextnowmod.so $(objpfx)tst-armtlsdescextlazy: $(objpfx)tst-armtlsdescextlazymod.so endif endif + +ifeq ($(enable-ubsan),yes) +# aeabi_unwind_cpp_pr1 is built as rtld module +CFLAGS-aeabi_unwind_cpp_pr1.c += -DDISABLE_USAN_INTERNAL_REDIR +endif endif ifeq ($(subdir),csu) diff --git a/sysdeps/generic/ldconfig.h b/sysdeps/generic/ldconfig.h index 7bc8788647c..78e43d70388 100644 --- a/sysdeps/generic/ldconfig.h +++ b/sysdeps/generic/ldconfig.h @@ -84,7 +84,8 @@ extern int search_aux_cache (struct stat *stat_buf, int *flags, extern void add_to_aux_cache (struct stat *stat_buf, int flags, unsigned int isa_level, const char *soname); -extern void save_aux_cache (const char *aux_cache_name); +extern void save_aux_cache (const char *aux_cache_name) + __attribute__((nonnull (1))); /* Declared in readlib.c. */ extern int process_file (const char *real_file_name, const char *file_name, diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index fc4a3de7678..527381222f8 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -773,6 +774,8 @@ extern void _dl_debug_printf (const char *fmt, ...) extern void _dl_debug_printf_c (const char *fmt, ...) __attribute__ ((__format__ (__printf__, 1, 2))) attribute_hidden; +extern void _dl_debug_vprintf_c (const char *fmt, va_list ap) + __attribute__ ((__format__ (__printf__, 1, 0))) attribute_hidden; /* Write a message on the specified descriptor FD. The parameters are interpreted as for a `printf' call. */ diff --git a/sysdeps/generic/symbol-hacks.h b/sysdeps/generic/symbol-hacks.h index 1115e4c0a7a..10a3a5388de 100644 --- a/sysdeps/generic/symbol-hacks.h +++ b/sysdeps/generic/symbol-hacks.h @@ -18,3 +18,39 @@ asm (".hidden __stack_chk_fail_local\n" "__stack_chk_fail = __stack_chk_fail_local"); # endif #endif + +#if !defined __ASSEMBLER__ && IS_IN(rtld) && defined ENABLE_UBSAN \ + && !defined DISABLE_USAN_INTERNAL_REDIR +/* These are autogenerated by the compiler, so no subject to either + hidden_attribute or hidden_proto alias definition. */ +asm ("__ubsan_handle_negate_overflow = " + "__GI___ubsan_handle_negate_overflow"); +asm ("__ubsan_handle_shift_out_of_bounds = " + "__GI___ubsan_handle_shift_out_of_bounds"); +asm ("__ubsan_handle_divrem_overflow = " + "__GI___ubsan_handle_divrem_overflow"); +asm ("__ubsan_handle_vla_bound_not_positive = " + "__GI___ubsan_handle_vla_bound_not_positive"); +asm ("__ubsan_handle_pointer_overflow = " + "__GI___ubsan_handle_pointer_overflow"); +asm ("__ubsan_handle_load_invalid_value =" + "__GI___ubsan_handle_load_invalid_value"); +asm ("__ubsan_handle_out_of_bounds = " + "__GI___ubsan_handle_out_of_bounds"); +asm ("__ubsan_handle_sub_overflow = " + "__GI___ubsan_handle_sub_overflow"); +asm ("__ubsan_handle_add_overflow = " + "__GI___ubsan_handle_add_overflow"); +asm ("__ubsan_handle_mul_overflow = " + "__GI___ubsan_handle_mul_overflow"); +asm ("__ubsan_handle_type_mismatch_v1 = " + "__GI___ubsan_handle_type_mismatch_v1"); +asm ("__ubsan_handle_nonnull_return_v1 = " + "__GI___ubsan_handle_nonnull_return_v1"); +asm ("__ubsan_handle_nonnull_arg = " + "__GI___ubsan_handle_nonnull_arg"); +asm ("__ubsan_handle_invalid_builtin = " + "__GI___ubsan_handle_invalid_builtin"); +asm ("__ubsan_handle_builtin_unreachable = " + "__GI___ubsan_handle_builtin_unreachable"); +#endif diff --git a/sysdeps/powerpc/powerpc64/multiarch/stpncpy-ppc64.c b/sysdeps/powerpc/powerpc64/multiarch/stpncpy-ppc64.c index e0a87478849..6dec085cb75 100644 --- a/sysdeps/powerpc/powerpc64/multiarch/stpncpy-ppc64.c +++ b/sysdeps/powerpc/powerpc64/multiarch/stpncpy-ppc64.c @@ -20,7 +20,8 @@ #ifdef SHARED #undef libc_hidden_def #define libc_hidden_def(name) \ - __hidden_ver1 (__stpncpy_ppc, __GI___stpncpy, __stpncpy_ppc); + __hidden_ver1 (__stpncpy_ppc, __GI___stpncpy, __stpncpy_ppc); \ + weak_alias (__stpncpy_ppc, __stpncpy) #endif #include -- 2.47.2