[SafeStack] Introduce public interface header and deprecate builtins (#198292)

Introduce `sanitizer/safestack_interface.h` to expose SafeStack utility
functions as a public interface, similar to other sanitizers.

Exposed functions:
* `__safestack_get_unsafe_stack_ptr`
* `__safestack_get_unsafe_stack_bottom`
* `__safestack_get_unsafe_stack_top`

Deprecate the existing `__builtin___get_unsafe_stack_*` builtins and
emit warnings suggesting the new `__safestack_` equivalents.

Runtime compatibility aliases `__get_unsafe_stack_*` are maintained.
This commit is contained in:
Jakob Koschel 2026-06-09 09:39:34 +02:00 committed by GitHub
parent 6b90b6b4a3
commit 69215c5e4f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 147 additions and 28 deletions

View file

@ -325,6 +325,11 @@ Non-comprehensive list of changes in this release
- Updated support for Unicode from 15.1 to 18.0.
- The SafeStack builtins ``__builtin___get_unsafe_stack_ptr``,
``__builtin___get_unsafe_stack_bottom``, ``__builtin___get_unsafe_stack_top``,
and ``__builtin___get_unsafe_stack_start`` are now deprecated. Use the
corresponding functions from ``<sanitizer/safestack_interface.h>`` instead.
New Compiler Flags
------------------
- New option ``-fms-anonymous-structs`` / ``-fno-ms-anonymous-structs`` added

View file

@ -159,29 +159,56 @@ these variables, so extra care must be taken to manually ensure that all such
accesses are safe. Furthermore, the addresses of such local variables should
never be stored on the heap, as it would leak the location of the SafeStack.
``sanitizer/safestack_interface.h``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To use the low-level API, include the header ``<sanitizer/safestack_interface.h>``.
``__safestack_get_unsafe_stack_ptr()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This function returns current unsafe stack pointer of the current thread.
``__safestack_get_unsafe_stack_bottom()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This function returns a pointer to the bottom of the unsafe stack of the
current thread.
``__safestack_get_unsafe_stack_top()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This function returns a pointer to the top of the unsafe stack of the
current thread.
Deprecated Builtins
~~~~~~~~~~~~~~~~~~~
The following builtins are still supported but are deprecated and will emit a
warning. Use the corresponding functions from ``<sanitizer/safestack_interface.h>``
instead.
``__builtin___get_unsafe_stack_ptr()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This builtin function returns current unsafe stack pointer of the current
thread.
Deprecated alias for ``__safestack_get_unsafe_stack_ptr()``.
``__builtin___get_unsafe_stack_bottom()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This builtin function returns a pointer to the bottom of the unsafe stack of the
current thread.
Deprecated alias for ``__safestack_get_unsafe_stack_bottom()``.
``__builtin___get_unsafe_stack_top()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This builtin function returns a pointer to the top of the unsafe stack of the
current thread.
Deprecated alias for ``__safestack_get_unsafe_stack_top()``.
``__builtin___get_unsafe_stack_start()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Deprecated: This builtin function is an alias for
``__builtin___get_unsafe_stack_bottom()``.
Deprecated alias for ``__safestack_get_unsafe_stack_bottom()``.
Design
======

View file

@ -2920,6 +2920,22 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
FPOptions FPO;
switch (BuiltinID) {
case Builtin::BI__builtin___get_unsafe_stack_start:
case Builtin::BI__builtin___get_unsafe_stack_bottom:
Diag(TheCall->getBeginLoc(), diag::warn_deprecated_builtin)
<< Context.BuiltinInfo.getQuotedName(BuiltinID)
<< "__safestack_get_unsafe_stack_bottom";
break;
case Builtin::BI__builtin___get_unsafe_stack_top:
Diag(TheCall->getBeginLoc(), diag::warn_deprecated_builtin)
<< Context.BuiltinInfo.getQuotedName(BuiltinID)
<< "__safestack_get_unsafe_stack_top";
break;
case Builtin::BI__builtin___get_unsafe_stack_ptr:
Diag(TheCall->getBeginLoc(), diag::warn_deprecated_builtin)
<< Context.BuiltinInfo.getQuotedName(BuiltinID)
<< "__safestack_get_unsafe_stack_ptr";
break;
case Builtin::BI__builtin_cpu_supports:
case Builtin::BI__builtin_cpu_is:
if (BuiltinCpu(*this, Context.getTargetInfo(), TheCall,

View file

@ -0,0 +1,17 @@
// RUN: %clang_cc1 %s -fsyntax-only -verify -fsanitize=safe-stack
void *test_bottom(void) {
return __builtin___get_unsafe_stack_bottom(); // expected-warning {{builtin '__builtin___get_unsafe_stack_bottom' is deprecated; use __safestack_get_unsafe_stack_bottom instead}}
}
void *test_top(void) {
return __builtin___get_unsafe_stack_top(); // expected-warning {{builtin '__builtin___get_unsafe_stack_top' is deprecated; use __safestack_get_unsafe_stack_top instead}}
}
void *test_ptr(void) {
return __builtin___get_unsafe_stack_ptr(); // expected-warning {{builtin '__builtin___get_unsafe_stack_ptr' is deprecated; use __safestack_get_unsafe_stack_ptr instead}}
}
void *test_start(void) {
return __builtin___get_unsafe_stack_start(); // expected-warning {{builtin '__builtin___get_unsafe_stack_start' is deprecated; use __safestack_get_unsafe_stack_bottom instead}}
}

View file

@ -11,6 +11,7 @@ if (COMPILER_RT_BUILD_SANITIZERS)
sanitizer/msan_interface.h
sanitizer/netbsd_syscall_hooks.h
sanitizer/rtsan_interface.h
sanitizer/safestack_interface.h
sanitizer/scudo_interface.h
sanitizer/tsan_interface.h
sanitizer/tsan_interface_atomic.h

View file

@ -0,0 +1,35 @@
//===-- sanitizer/safestack_interface.h -------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of SafeStack.
//
// Public interface header.
//===----------------------------------------------------------------------===//
#ifndef SANITIZER_SAFESTACK_INTERFACE_H
#define SANITIZER_SAFESTACK_INTERFACE_H
#include <sanitizer/common_interface_defs.h>
#ifdef __cplusplus
extern "C" {
#endif
/// Returns the current unsafe stack pointer of the current thread.
const void *SANITIZER_CDECL __safestack_get_unsafe_stack_ptr(void);
/// Returns a pointer to the bottom of the unsafe stack of the current thread.
const void *SANITIZER_CDECL __safestack_get_unsafe_stack_bottom(void);
/// Returns a pointer to the top of the unsafe stack of the current thread.
const void *SANITIZER_CDECL __safestack_get_unsafe_stack_top(void);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // SANITIZER_SAFESTACK_INTERFACE_H

View file

@ -5,6 +5,7 @@ set(SAFESTACK_SOURCES
)
include_directories(..)
include_directories(../../include)
set(SAFESTACK_CFLAGS ${SANITIZER_COMMON_CFLAGS})

View file

@ -22,6 +22,7 @@
#include "interception/interception.h"
#include "safestack_platform.h"
#include "safestack_util.h"
#include "sanitizer/safestack_interface.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
// interception.h drags in sanitizer_redefine_builtins.h, which in turn
@ -338,22 +339,34 @@ __attribute__((section(".preinit_array"),
}
#endif
extern "C"
__attribute__((visibility("default"))) void *__get_unsafe_stack_bottom() {
extern "C" SANITIZER_INTERFACE_ATTRIBUTE const void*
__safestack_get_unsafe_stack_bottom() {
return unsafe_stack_start;
}
extern "C"
__attribute__((visibility("default"))) void *__get_unsafe_stack_top() {
extern "C" SANITIZER_INTERFACE_ATTRIBUTE const void*
__safestack_get_unsafe_stack_top() {
return (char*)unsafe_stack_start + unsafe_stack_size;
}
extern "C"
__attribute__((visibility("default"))) void *__get_unsafe_stack_start() {
return unsafe_stack_start;
}
extern "C"
__attribute__((visibility("default"))) void *__get_unsafe_stack_ptr() {
extern "C" SANITIZER_INTERFACE_ATTRIBUTE const void*
__safestack_get_unsafe_stack_ptr() {
return __safestack_unsafe_stack_ptr;
}
// Compatibility aliases
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void* __get_unsafe_stack_bottom() {
return const_cast<void*>(__safestack_get_unsafe_stack_bottom());
}
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void* __get_unsafe_stack_top() {
return const_cast<void*>(__safestack_get_unsafe_stack_top());
}
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void* __get_unsafe_stack_start() {
return const_cast<void*>(__safestack_get_unsafe_stack_bottom());
}
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void* __get_unsafe_stack_ptr() {
return const_cast<void*>(__safestack_get_unsafe_stack_ptr());
}

View file

@ -15,22 +15,24 @@
// Test that safe stack works with sigaltstack.
int puts(const char *);
extern void *__get_unsafe_stack_ptr();
extern void *__get_unsafe_stack_top();
extern void *__get_unsafe_stack_bottom();
#include <sanitizer/safestack_interface.h>
__thread int signal_handlers_called = 0;
void signal_handler(int signo) {
signal_handlers_called += 1;
assert(__get_unsafe_stack_ptr() <= __get_unsafe_stack_top() &&
__get_unsafe_stack_ptr() >= __get_unsafe_stack_bottom());
assert(__safestack_get_unsafe_stack_ptr() <=
__safestack_get_unsafe_stack_top() &&
__safestack_get_unsafe_stack_ptr() >=
__safestack_get_unsafe_stack_bottom());
}
void signal_sigaction(int signo, siginfo_t *si, void *uc) {
signal_handlers_called += 1;
assert(__get_unsafe_stack_ptr() <= __get_unsafe_stack_top() &&
__get_unsafe_stack_ptr() >= __get_unsafe_stack_bottom());
assert(__safestack_get_unsafe_stack_ptr() <=
__safestack_get_unsafe_stack_top() &&
__safestack_get_unsafe_stack_ptr() >=
__safestack_get_unsafe_stack_bottom());
}
void *t1_start(void *ptr) {
@ -113,8 +115,10 @@ int main() {
raise(SIGUSR2);
// Check that unsafe stack is set to the normal unsafe stack.
assert(__get_unsafe_stack_ptr() <= __get_unsafe_stack_top() &&
__get_unsafe_stack_ptr() >= __get_unsafe_stack_bottom());
assert(__safestack_get_unsafe_stack_ptr() <=
__safestack_get_unsafe_stack_top() &&
__safestack_get_unsafe_stack_ptr() >=
__safestack_get_unsafe_stack_bottom());
assert(signal_handlers_called == 4);