22 changes: 12 additions & 10 deletions compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,29 +50,30 @@ static bool HandleDynamicTypeCacheMiss(

ScopedReport R(Opts, Loc, ET);

Diag(Loc, DL_Error,
Diag(Loc, DL_Error, ET,
"%0 address %1 which does not point to an object of type %2")
<< TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type;

// If possible, say what type it actually points to.
if (!DTI.isValid()) {
if (DTI.getOffset() < -VptrMaxOffsetToTop || DTI.getOffset() > VptrMaxOffsetToTop) {
Diag(Pointer, DL_Note, "object has a possibly invalid vptr: abs(offset to top) too big")
Diag(Pointer, DL_Note, ET,
"object has a possibly invalid vptr: abs(offset to top) too big")
<< TypeName(DTI.getMostDerivedTypeName())
<< Range(Pointer, Pointer + sizeof(uptr), "possibly invalid vptr");
} else {
Diag(Pointer, DL_Note, "object has invalid vptr")
Diag(Pointer, DL_Note, ET, "object has invalid vptr")
<< TypeName(DTI.getMostDerivedTypeName())
<< Range(Pointer, Pointer + sizeof(uptr), "invalid vptr");
}
} else if (!DTI.getOffset())
Diag(Pointer, DL_Note, "object is of type %0")
Diag(Pointer, DL_Note, ET, "object is of type %0")
<< TypeName(DTI.getMostDerivedTypeName())
<< Range(Pointer, Pointer + sizeof(uptr), "vptr for %0");
else
// FIXME: Find the type at the specified offset, and include that
// in the note.
Diag(Pointer - DTI.getOffset(), DL_Note,
Diag(Pointer - DTI.getOffset(), DL_Note, ET,
"object is base class subobject at offset %0 within object of type %1")
<< DTI.getOffset() << TypeName(DTI.getMostDerivedTypeName())
<< TypeName(DTI.getSubobjectTypeName())
Expand Down Expand Up @@ -126,19 +127,20 @@ void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable,
Die();
}

Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during "
"%1 (vtable address %2)")
Diag(Loc, DL_Error, ET,
"control flow integrity check for type %0 failed during "
"%1 (vtable address %2)")
<< Data->Type << CheckKindStr << (void *)Vtable;

// If possible, say what type it actually points to.
if (!DTI.isValid()) {
const char *module = Symbolizer::GetOrInit()->GetModuleNameForPc(Vtable);
if (module)
Diag(Vtable, DL_Note, "invalid vtable in module %0") << module;
Diag(Vtable, DL_Note, ET, "invalid vtable in module %0") << module;
else
Diag(Vtable, DL_Note, "invalid vtable");
Diag(Vtable, DL_Note, ET, "invalid vtable");
} else {
Diag(Vtable, DL_Note, "vtable is of type %0")
Diag(Vtable, DL_Note, ET, "vtable is of type %0")
<< TypeName(DTI.getMostDerivedTypeName());
}
}
Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/lib/ubsan/ubsan_interface.inc
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,5 @@ INTERFACE_FUNCTION(__ubsan_handle_type_mismatch_v1_abort)
INTERFACE_FUNCTION(__ubsan_handle_vla_bound_not_positive)
INTERFACE_FUNCTION(__ubsan_handle_vla_bound_not_positive_abort)
INTERFACE_WEAK_FUNCTION(__ubsan_default_options)
INTERFACE_WEAK_FUNCTION(__ubsan_on_report)
INTERFACE_FUNCTION(__ubsan_get_current_report_data)
76 changes: 76 additions & 0 deletions compiler-rt/lib/ubsan/ubsan_monitor.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//===-- ubsan_monitor.cc ----------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Hooks which allow a monitor process to inspect UBSan's diagnostics.
//
//===----------------------------------------------------------------------===//

#include "ubsan_monitor.h"

using namespace __ubsan;

UndefinedBehaviorReport::UndefinedBehaviorReport(const char *IssueKind,
Location &Loc,
InternalScopedString &Msg)
: IssueKind(IssueKind), Loc(Loc), Buffer(Msg.length() + 1) {
// We have the common sanitizer reporting lock, so it's safe to register a
// new UB report.
RegisterUndefinedBehaviorReport(this);

// Make a copy of the diagnostic.
Buffer.append("%s", Msg.data());

// Let the monitor know that a report is available.
__ubsan_on_report();
}

static UndefinedBehaviorReport *CurrentUBR;

void __ubsan::RegisterUndefinedBehaviorReport(UndefinedBehaviorReport *UBR) {
CurrentUBR = UBR;
}

SANITIZER_WEAK_DEFAULT_IMPL
void __ubsan::__ubsan_on_report(void) {}

void __ubsan::__ubsan_get_current_report_data(const char **OutIssueKind,
const char **OutMessage,
const char **OutFilename,
unsigned *OutLine,
unsigned *OutCol,
char **OutMemoryAddr) {
if (!OutIssueKind || !OutMessage || !OutFilename || !OutLine || !OutCol ||
!OutMemoryAddr)
UNREACHABLE("Invalid arguments passed to __ubsan_get_current_report_data");

InternalScopedString &Buf = CurrentUBR->Buffer;

// Ensure that the first character of the diagnostic text can't start with a
// lowercase letter.
char FirstChar = Buf.data()[0];
if (FirstChar >= 'a' && FirstChar <= 'z')
Buf.data()[0] = FirstChar - 'a' + 'A';

*OutIssueKind = CurrentUBR->IssueKind;
*OutMessage = Buf.data();
if (!CurrentUBR->Loc.isSourceLocation()) {
*OutFilename = "<unknown>";
*OutLine = *OutCol = 0;
} else {
SourceLocation SL = CurrentUBR->Loc.getSourceLocation();
*OutFilename = SL.getFilename();
*OutLine = SL.getLine();
*OutCol = SL.getColumn();
}

if (CurrentUBR->Loc.isMemoryLocation())
*OutMemoryAddr = (char *)CurrentUBR->Loc.getMemoryLocation();
else
*OutMemoryAddr = nullptr;
}
49 changes: 49 additions & 0 deletions compiler-rt/lib/ubsan/ubsan_monitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//===-- ubsan_monitor.h -----------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Hooks which allow a monitor process to inspect UBSan's diagnostics.
//
//===----------------------------------------------------------------------===//

#ifndef UBSAN_MONITOR_H
#define UBSAN_MONITOR_H

#include "ubsan_diag.h"
#include "ubsan_value.h"

namespace __ubsan {

struct UndefinedBehaviorReport {
const char *IssueKind;
Location &Loc;
InternalScopedString Buffer;

UndefinedBehaviorReport(const char *IssueKind, Location &Loc,
InternalScopedString &Msg);
};

SANITIZER_INTERFACE_ATTRIBUTE void
RegisterUndefinedBehaviorReport(UndefinedBehaviorReport *UBR);

/// Called after a report is prepared. This serves to alert monitor processes
/// that a UB report is available.
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __ubsan_on_report(void);

/// Used by the monitor process to extract information from a UB report. The
/// data is only available until the next time __ubsan_on_report is called. The
/// caller is responsible for copying and preserving the data if needed.
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__ubsan_get_current_report_data(const char **OutIssueKind,
const char **OutMessage,
const char **OutFilename, unsigned *OutLine,
unsigned *OutCol, char **OutMemoryAddr);

} // end namespace __ubsan

#endif // UBSAN_MONITOR_H
37 changes: 37 additions & 0 deletions compiler-rt/test/ubsan/TestCases/Misc/monitor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// RUN: %clangxx -w -fsanitize=bool %s -o %t
// RUN: %run %t 2>&1 | FileCheck %s

#include <iostream>

extern "C" {
void __ubsan_get_current_report_data(const char **OutIssueKind,
const char **OutMessage,
const char **OutFilename,
unsigned *OutLine, unsigned *OutCol,
char **OutMemoryAddr);

// Override the weak definition of __ubsan_on_report from the runtime, just
// for testing purposes.
void __ubsan_on_report(void) {
const char *IssueKind, *Message, *Filename;
unsigned Line, Col;
char *Addr;
__ubsan_get_current_report_data(&IssueKind, &Message, &Filename, &Line, &Col,
&Addr);

std::cout << "Issue: " << IssueKind << "\n"
<< "Location: " << Filename << ":" << Line << ":" << Col << "\n"
<< "Message: " << Message << std::endl;

(void)Addr;
}
}

int main() {
char C = 3;
bool B = *(bool *)&C;
// CHECK: Issue: invalid-bool-load
// CHECK-NEXT: Location: {{.*}}monitor.cpp:[[@LINE-2]]:12
// CHECK-NEXT: Message: Load of value 3, which is not a valid value for type 'bool'
return 0;
}