https://github.com/localspook updated https://github.com/llvm/llvm-project/pull/165181
>From f6574b6b59b91ce299fa6977b2b326aae0bf235d Mon Sep 17 00:00:00 2001 From: Victor Chernyakin <[email protected]> Date: Sun, 26 Oct 2025 16:57:54 -0700 Subject: [PATCH] [UBSan] Add check that detects division by zero for fixed-point types --- clang/docs/ReleaseNotes.rst | 2 +- clang/docs/UndefinedBehaviorSanitizer.rst | 4 ++++ clang/include/clang/Basic/Features.def | 4 ++++ clang/include/clang/Basic/Sanitizers.def | 4 +++- clang/lib/CodeGen/CGExpr.cpp | 8 ++++++-- clang/lib/CodeGen/CGExprScalar.cpp | 13 ++++++++++++- .../has_feature_undefined_behavior_sanitizer.cpp | 16 ++++++++++++++++ compiler-rt/lib/ubsan/ubsan_checks.inc | 2 ++ compiler-rt/lib/ubsan/ubsan_handlers.cpp | 2 ++ compiler-rt/lib/ubsan/ubsan_value.h | 4 ++++ .../TestCases/FixedPoint/divide-by-zero.cpp | 10 ++++++++++ 11 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 compiler-rt/test/ubsan/TestCases/FixedPoint/divide-by-zero.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index edb872c1f388d..b4a5f7dcbfa20 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -274,7 +274,7 @@ New Compiler Flags - New option ``-fno-sanitize-debug-trap-reasons`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``). - New option ``-fsanitize-debug-trap-reasons=`` added to control emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``). - New options for enabling allocation token instrumentation: ``-fsanitize=alloc-token``, ``-falloc-token-max=``, ``-fsanitize-alloc-token-fast-abi``, ``-fsanitize-alloc-token-extended``. - +- New option for diagnosing division by zero with fixed point numbers (``-ffixed-point``): ``-fsanitize=fixed-point-divide-by-zero``, and a new group that includes it: ``-fsanitize=fixed-point``. Lanai Support ^^^^^^^^^^^^^^ diff --git a/clang/docs/UndefinedBehaviorSanitizer.rst b/clang/docs/UndefinedBehaviorSanitizer.rst index 0a2d833783e57..568d3f092de3e 100644 --- a/clang/docs/UndefinedBehaviorSanitizer.rst +++ b/clang/docs/UndefinedBehaviorSanitizer.rst @@ -119,6 +119,8 @@ Available checks are: - ``-fsanitize=enum``: Load of a value of an enumerated type which is not in the range of representable values for that enumerated type. + - ``-fsanitize=fixed-point-divide-by-zero``: Fixed point division by zero + (when compiling with ``-ffixed-point``). - ``-fsanitize=float-cast-overflow``: Conversion to, from, or between floating-point types which would overflow the destination. Because the range of representable values for all @@ -224,6 +226,8 @@ You can also use the following check groups: ``nullability-*`` group of checks. - ``-fsanitize=undefined-trap``: Deprecated alias of ``-fsanitize=undefined``. + - ``-fsanitize=fixed-point``: Checks for undefined behavior with fixed point + values. Enables ``-fsanitize=fixed-point-divide-by-zero``. - ``-fsanitize=implicit-integer-truncation``: Catches lossy integral conversions. Enables ``implicit-signed-integer-truncation`` and ``implicit-unsigned-integer-truncation``. diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 0e91b42a132c1..204150627fd8f 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -66,6 +66,10 @@ FEATURE(array_bounds_sanitizer, LangOpts.Sanitize.has(SanitizerKind::ArrayBounds)) FEATURE(enum_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Enum)) +FEATURE(fixed_point_divide_by_zero_sanitizer, + LangOpts.Sanitize.has(SanitizerKind::FixedPointDivideByZero)) +FEATURE(fixed_point_sanitizer, + LangOpts.Sanitize.hasOneOf(SanitizerKind::FixedPoint)) FEATURE(float_cast_overflow_sanitizer, LangOpts.Sanitize.has(SanitizerKind::FloatCastOverflow)) FEATURE(integer_divide_by_zero_sanitizer, diff --git a/clang/include/clang/Basic/Sanitizers.def b/clang/include/clang/Basic/Sanitizers.def index da85431625026..b77a838d71da1 100644 --- a/clang/include/clang/Basic/Sanitizers.def +++ b/clang/include/clang/Basic/Sanitizers.def @@ -94,6 +94,8 @@ SANITIZER("array-bounds", ArrayBounds) SANITIZER("bool", Bool) SANITIZER("builtin", Builtin) SANITIZER("enum", Enum) +SANITIZER("fixed-point-divide-by-zero", FixedPointDivideByZero) +SANITIZER_GROUP("fixed-point", FixedPoint, FixedPointDivideByZero) SANITIZER("float-cast-overflow", FloatCastOverflow) SANITIZER("float-divide-by-zero", FloatDivideByZero) SANITIZER("function", Function) @@ -149,7 +151,7 @@ SANITIZER("shadow-call-stack", ShadowCallStack) // ABI or address space layout implications, and only catch undefined behavior. SANITIZER_GROUP("undefined", Undefined, Alignment | Bool | Builtin | ArrayBounds | Enum | - FloatCastOverflow | + FixedPoint | FloatCastOverflow | IntegerDivideByZero | NonnullAttribute | Null | ObjectSize | PointerOverflow | Return | ReturnsNonnullAttribute | Shift | SignedIntegerOverflow | Unreachable | VLABound | Function) diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index e8255b0554da8..02876e72f83cb 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -79,6 +79,8 @@ enum VariableTypeDescriptorKind : uint16_t { TK_Float = 0x0001, /// An _BitInt(N) type. TK_BitInt = 0x0002, + /// A fixed-point type. + TK_FixedPoint = 0x0003, /// Any other type. The value representation is unspecified. TK_Unknown = 0xffff }; @@ -3724,8 +3726,8 @@ LValue CodeGenFunction::EmitPredefinedLValue(const PredefinedExpr *E) { /// /// followed by an array of i8 containing the type name with extra information /// for BitInt. TypeKind is TK_Integer(0) for an integer, TK_Float(1) for a -/// floating point value, TK_BitInt(2) for BitInt and TK_Unknown(0xFFFF) for -/// anything else. +/// floating point value, TK_BitInt(2) for BitInt, TK_FixedPoint(3) for a +// fixed point value, and TK_Unknown(0xFFFF) for anything else. llvm::Constant *CodeGenFunction::EmitCheckTypeDescriptor(QualType T) { // Only emit each type's descriptor once. if (llvm::Constant *C = CGM.getTypeDescriptorFromMap(T)) @@ -3757,6 +3759,8 @@ llvm::Constant *CodeGenFunction::EmitCheckTypeDescriptor(QualType T) { } else if (T->isFloatingType()) { TypeKind = TK_Float; TypeInfo = getContext().getTypeSize(T); + } else if (T->isFixedPointType()) { + TypeKind = TK_FixedPoint; } // Format the type name as if for a diagnostic, including quotes and diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 715160d067817..65474a3f14d13 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -4591,9 +4591,20 @@ Value *ScalarExprEmitter::EmitFixedPointBinOp(const BinOpInfo &op) { Result = FPBuilder.CreateMul(LHS, LHSFixedSema, RHS, RHSFixedSema); break; case BO_DivAssign: - case BO_Div: + case BO_Div: { + SanitizerDebugLocation SanScope(&CGF, + {SanitizerKind::SO_FixedPointDivideByZero}, + SanitizerHandler::DivremOverflow); + if (CGF.SanOpts.has(SanitizerKind::FixedPointDivideByZero)) { + Value *Zero = llvm::Constant::getNullValue(RHS->getType()); + const std::pair<Value *, SanitizerKind::SanitizerOrdinal> Check = { + Builder.CreateICmpNE(RHS, Zero), + SanitizerKind::SO_FixedPointDivideByZero}; + EmitBinOpCheck(Check, op); + } Result = FPBuilder.CreateDiv(LHS, LHSFixedSema, RHS, RHSFixedSema); break; + } case BO_ShlAssign: case BO_Shl: Result = FPBuilder.CreateShl(LHS, LHSFixedSema, RHS); diff --git a/clang/test/Lexer/has_feature_undefined_behavior_sanitizer.cpp b/clang/test/Lexer/has_feature_undefined_behavior_sanitizer.cpp index e1a07d215b549..e1f9827bfed0f 100644 --- a/clang/test/Lexer/has_feature_undefined_behavior_sanitizer.cpp +++ b/clang/test/Lexer/has_feature_undefined_behavior_sanitizer.cpp @@ -4,6 +4,8 @@ // RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=builtin %s -o - | FileCheck --check-prefixes=CHECK-UBSAN,CHECK-BUILTIN %s // RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=array-bounds %s -o - | FileCheck --check-prefixes=CHECK-UBSAN,CHECK-ARRAY-BOUNDS %s // RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=enum %s -o - | FileCheck --check-prefixes=CHECK-UBSAN,CHECK-ENUM %s +// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=fixed-point-divide-by-zero %s -o - | FileCheck --check-prefixes=CHECK-UBSAN,CHECK-FIXED-POINT-DIVIDE-BY-ZERO,CHECK-FIXED-POINT %s +// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=fixed-point %s -o - | FileCheck --check-prefixes=CHECK-UBSAN,CHECK-FIXED-POINT %s // RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=float-cast-overflow %s -o - | FileCheck --check-prefixes=CHECK-UBSAN,CHECK-FLOAT-CAST-OVERFLOW %s // RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=integer-divide-by-zero %s -o - | FileCheck --check-prefixes=CHECK-UBSAN,CHECK-INTEGER-DIVIDE-BY-ZERO %s // RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=nonnull-attribute %s -o - | FileCheck --check-prefixes=CHECK-UBSAN,CHECK-NONNULL-ATTRIBUTE %s @@ -65,6 +67,18 @@ int EnumSanitizerEnabled(); int EnumSanitizerDisabled(); #endif +#if __has_feature(fixed_point_divide_by_zero_sanitizer) +int FixedPointDivideByZeroSanitizerEnabled(); +#else +int FixedPointDivideByZeroSanitizerDisabled(); +#endif + +#if __has_feature(fixed_point_sanitizer) +int FixedPointSanitizerEnabled(); +#else +int FixedPointSanitizerDisabled(); +#endif + #if __has_feature(float_cast_overflow_sanitizer) int FloatCastOverflowSanitizerEnabled(); #else @@ -161,6 +175,8 @@ int FunctionSanitizerDisabled(); // CHECK-BUILTIN: BuiltinSanitizerEnabled // CHECK-ARRAY-BOUNDS: ArrayBoundsSanitizerEnabled // CHECK-ENUM: EnumSanitizerEnabled +// CHECK-FIXED-POINT-DIVIDE-BY-ZERO: FixedPointDivideByZeroSanitizerEnabled +// CHECK-FIXED-POINT: FixedPointSanitizerEnabled // CHECK-FLOAT-CAST-OVERFLOW: FloatCastOverflowSanitizerEnabled // CHECK-INTEGER-DIVIDE-BY-ZERO: IntegerDivideByZeroSanitizerEnabled // CHECK-NONNULL-ATTRIBUTE: NonnullAttributeSanitizerEnabled diff --git a/compiler-rt/lib/ubsan/ubsan_checks.inc b/compiler-rt/lib/ubsan/ubsan_checks.inc index b1d09a9024e7e..9fe0291cab9bc 100644 --- a/compiler-rt/lib/ubsan/ubsan_checks.inc +++ b/compiler-rt/lib/ubsan/ubsan_checks.inc @@ -36,6 +36,8 @@ UBSAN_CHECK(UnsignedIntegerOverflow, "unsigned-integer-overflow", UBSAN_CHECK(IntegerDivideByZero, "integer-divide-by-zero", "integer-divide-by-zero") UBSAN_CHECK(FloatDivideByZero, "float-divide-by-zero", "float-divide-by-zero") +UBSAN_CHECK(FixedPointDivideByZero, "fixed-point-divide-by-zero", + "fixed-point-divide-by-zero") UBSAN_CHECK(InvalidBuiltin, "invalid-builtin-use", "invalid-builtin-use") UBSAN_CHECK(InvalidObjCCast, "invalid-objc-cast", "invalid-objc-cast") UBSAN_CHECK(ImplicitUnsignedIntegerTruncation, diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.cpp b/compiler-rt/lib/ubsan/ubsan_handlers.cpp index 63319f46734a4..c266298c86295 100644 --- a/compiler-rt/lib/ubsan/ubsan_handlers.cpp +++ b/compiler-rt/lib/ubsan/ubsan_handlers.cpp @@ -294,6 +294,8 @@ static void handleDivremOverflowImpl(OverflowData *Data, ValueHandle LHS, ET = ErrorType::SignedIntegerOverflow; else if (Data->Type.isIntegerTy()) ET = ErrorType::IntegerDivideByZero; + else if (Data->Type.isFixedPointTy()) + ET = ErrorType::FixedPointDivideByZero; else ET = ErrorType::FloatDivideByZero; diff --git a/compiler-rt/lib/ubsan/ubsan_value.h b/compiler-rt/lib/ubsan/ubsan_value.h index ee523cf5ddda5..812cde9558076 100644 --- a/compiler-rt/lib/ubsan/ubsan_value.h +++ b/compiler-rt/lib/ubsan/ubsan_value.h @@ -110,6 +110,8 @@ class TypeDescriptor { /// of the type for the signed _BitInt(N) type stored after zero bit after /// TypeName as 32-bit unsigned integer. TK_BitInt = 0x0002, + /// A fixed-point type. The value representation is currently empty. + TK_FixedPoint = 0x0003, /// Any other type. The value representation is unspecified. TK_Unknown = 0xffff }; @@ -164,6 +166,8 @@ class TypeDescriptor { CHECK(isFloatTy()); return TypeInfo; } + + bool isFixedPointTy() const { return getKind() == TK_FixedPoint; } }; /// \brief An opaque handle to a value. diff --git a/compiler-rt/test/ubsan/TestCases/FixedPoint/divide-by-zero.cpp b/compiler-rt/test/ubsan/TestCases/FixedPoint/divide-by-zero.cpp new file mode 100644 index 0000000000000..a7631491e37fa --- /dev/null +++ b/compiler-rt/test/ubsan/TestCases/FixedPoint/divide-by-zero.cpp @@ -0,0 +1,10 @@ +// RUN: %clangxx -DOP="0.0R / 0" -ffixed-point -fsanitize=fixed-point-divide-by-zero %s -o %t1 && %run %t1 2>&1 | FileCheck %s +// RUN: %clangxx -DOP="0.5R / 0" -ffixed-point -fsanitize=fixed-point-divide-by-zero %s -o %t2 && %run %t2 2>&1 | FileCheck %s +// RUN: %clangxx -DOP="0.0R / 0.0R" -ffixed-point -fsanitize=fixed-point-divide-by-zero %s -o %t3 && %run %t3 2>&1 | FileCheck %s +// RUN: %clangxx -DOP="0.5R / 0.0R" -ffixed-point -fsanitize=fixed-point-divide-by-zero %s -o %t4 && %run %t4 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=undefined -DOP="_Fract a = 0.5R; a /= 0" -ffixed-point -fsanitize=fixed-point-divide-by-zero %s -o %t5 && %run %t5 2>&1 | FileCheck %s + +int main() { + // CHECK: divide-by-zero.cpp:[[@LINE+1]]:3: runtime error: division by zero + OP; +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
