mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-29 08:31:13 +00:00
[lldb/DataFormatter] Check for overflow when finding NSDate epoch
Summary: Fixes UBSan-reported issues where the date value inside of an uninitialized NSDate overflows the 64-bit epoch. rdar://61774575 Reviewers: JDevlieghere, mib, teemperor Subscribers: mgorny, lldb-commits Tags: #lldb Differential Revision: https://reviews.llvm.org/D80150
This commit is contained in:
parent
47a0e9f49b
commit
b783f70a42
26
lldb/include/lldb/DataFormatters/Mock.h
Normal file
26
lldb/include/lldb/DataFormatters/Mock.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//===-- Mock.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
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLDB_DATAFORMATTERS_MOCK_H
|
||||||
|
#define LLDB_DATAFORMATTERS_MOCK_H
|
||||||
|
|
||||||
|
namespace lldb_private {
|
||||||
|
|
||||||
|
class Stream;
|
||||||
|
|
||||||
|
namespace formatters {
|
||||||
|
namespace NSDate {
|
||||||
|
|
||||||
|
/// Format the date_value field of a NSDate.
|
||||||
|
bool FormatDateValue(double date_value, Stream &stream);
|
||||||
|
|
||||||
|
} // namespace NSDate
|
||||||
|
} // namespace formatters
|
||||||
|
} // namespace lldb_private
|
||||||
|
|
||||||
|
#endif // LLDB_DATAFORMATTERS_MOCK_H
|
@ -13,6 +13,7 @@
|
|||||||
#include "lldb/Core/ValueObject.h"
|
#include "lldb/Core/ValueObject.h"
|
||||||
#include "lldb/Core/ValueObjectConstResult.h"
|
#include "lldb/Core/ValueObjectConstResult.h"
|
||||||
#include "lldb/DataFormatters/FormattersHelpers.h"
|
#include "lldb/DataFormatters/FormattersHelpers.h"
|
||||||
|
#include "lldb/DataFormatters/Mock.h"
|
||||||
#include "lldb/DataFormatters/StringPrinter.h"
|
#include "lldb/DataFormatters/StringPrinter.h"
|
||||||
#include "lldb/DataFormatters/TypeSummary.h"
|
#include "lldb/DataFormatters/TypeSummary.h"
|
||||||
#include "lldb/Host/Time.h"
|
#include "lldb/Host/Time.h"
|
||||||
@ -27,6 +28,7 @@
|
|||||||
|
|
||||||
#include "llvm/ADT/APInt.h"
|
#include "llvm/ADT/APInt.h"
|
||||||
#include "llvm/ADT/bit.h"
|
#include "llvm/ADT/bit.h"
|
||||||
|
#include "llvm/Support/CheckedArithmetic.h"
|
||||||
|
|
||||||
#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
|
#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
|
||||||
|
|
||||||
@ -785,6 +787,34 @@ static double decodeTaggedTimeInterval(uint64_t encodedTimeInterval) {
|
|||||||
return llvm::bit_cast<double>(decodedBits);
|
return llvm::bit_cast<double>(decodedBits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool lldb_private::formatters::NSDate::FormatDateValue(double date_value,
|
||||||
|
Stream &stream) {
|
||||||
|
if (date_value == -63114076800) {
|
||||||
|
stream.Printf("0001-12-30 00:00:00 +0000");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (date_value > std::numeric_limits<time_t>::max() ||
|
||||||
|
date_value < std::numeric_limits<time_t>::min())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
time_t epoch = GetOSXEpoch();
|
||||||
|
if (auto osx_epoch = llvm::checkedAdd(epoch, (time_t)date_value))
|
||||||
|
epoch = *osx_epoch;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
tm *tm_date = gmtime(&epoch);
|
||||||
|
if (!tm_date)
|
||||||
|
return false;
|
||||||
|
std::string buffer(1024, 0);
|
||||||
|
if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0)
|
||||||
|
return false;
|
||||||
|
stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900,
|
||||||
|
tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour,
|
||||||
|
tm_date->tm_min, tm_date->tm_sec, buffer.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool lldb_private::formatters::NSDateSummaryProvider(
|
bool lldb_private::formatters::NSDateSummaryProvider(
|
||||||
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
|
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
|
||||||
ProcessSP process_sp = valobj.GetProcessSP();
|
ProcessSP process_sp = valobj.GetProcessSP();
|
||||||
@ -828,6 +858,16 @@ bool lldb_private::formatters::NSDateSummaryProvider(
|
|||||||
if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) {
|
if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) {
|
||||||
date_value_bits = ((value_bits << 8) | (info_bits << 4));
|
date_value_bits = ((value_bits << 8) | (info_bits << 4));
|
||||||
memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
|
memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
|
||||||
|
|
||||||
|
// Accomodate the __NSTaggedDate format introduced in Foundation 1600.
|
||||||
|
if (class_name == g___NSTaggedDate) {
|
||||||
|
auto *apple_runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
|
||||||
|
ObjCLanguageRuntime::Get(*process_sp));
|
||||||
|
if (!apple_runtime)
|
||||||
|
return false;
|
||||||
|
if (apple_runtime->GetFoundationVersion() >= 1600)
|
||||||
|
date_value = decodeTaggedTimeInterval(value_bits << 4);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
llvm::Triple triple(
|
llvm::Triple triple(
|
||||||
process_sp->GetTarget().GetArchitecture().GetTriple());
|
process_sp->GetTarget().GetArchitecture().GetTriple());
|
||||||
@ -850,34 +890,7 @@ bool lldb_private::formatters::NSDateSummaryProvider(
|
|||||||
} else
|
} else
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (date_value == -63114076800) {
|
return NSDate::FormatDateValue(date_value, stream);
|
||||||
stream.Printf("0001-12-30 00:00:00 +0000");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accomodate for the __NSTaggedDate format introduced in Foundation 1600.
|
|
||||||
if (class_name == g___NSTaggedDate) {
|
|
||||||
auto *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
|
|
||||||
ObjCLanguageRuntime::Get(*process_sp));
|
|
||||||
if (runtime && runtime->GetFoundationVersion() >= 1600)
|
|
||||||
date_value = decodeTaggedTimeInterval(value_bits << 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
// this snippet of code assumes that time_t == seconds since Jan-1-1970 this
|
|
||||||
// is generally true and POSIXly happy, but might break if a library vendor
|
|
||||||
// decides to get creative
|
|
||||||
time_t epoch = GetOSXEpoch();
|
|
||||||
epoch = epoch + (time_t)date_value;
|
|
||||||
tm *tm_date = gmtime(&epoch);
|
|
||||||
if (!tm_date)
|
|
||||||
return false;
|
|
||||||
std::string buffer(1024, 0);
|
|
||||||
if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0)
|
|
||||||
return false;
|
|
||||||
stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900,
|
|
||||||
tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour,
|
|
||||||
tm_date->tm_min, tm_date->tm_sec, buffer.c_str());
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lldb_private::formatters::ObjCClassSummaryProvider(
|
bool lldb_private::formatters::ObjCClassSummaryProvider(
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
add_lldb_unittest(LLDBFormatterTests
|
add_lldb_unittest(LLDBFormatterTests
|
||||||
FormatManagerTests.cpp
|
FormatManagerTests.cpp
|
||||||
|
MockTests.cpp
|
||||||
StringPrinterTests.cpp
|
StringPrinterTests.cpp
|
||||||
|
|
||||||
LINK_LIBS
|
LINK_LIBS
|
||||||
|
40
lldb/unittests/DataFormatter/MockTests.cpp
Normal file
40
lldb/unittests/DataFormatter/MockTests.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//===-- MockTests.cpp -----------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "lldb/DataFormatters/Mock.h"
|
||||||
|
#include "lldb/Utility/StreamString.h"
|
||||||
|
#include "llvm/ADT/Optional.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace lldb_private;
|
||||||
|
|
||||||
|
// Try to format the date_value field of a NSDate.
|
||||||
|
static llvm::Optional<std::string> formatDateValue(double date_value) {
|
||||||
|
StreamString s;
|
||||||
|
bool succcess = formatters::NSDate::FormatDateValue(date_value, s);
|
||||||
|
if (succcess)
|
||||||
|
return std::string(s.GetData());
|
||||||
|
return llvm::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DataFormatterMockTest, NSDate) {
|
||||||
|
EXPECT_EQ(*formatDateValue(-63114076800), "0001-12-30 00:00:00 +0000");
|
||||||
|
|
||||||
|
// Can't convert the date_value to a time_t.
|
||||||
|
EXPECT_EQ(formatDateValue(std::numeric_limits<time_t>::max() + 1),
|
||||||
|
llvm::None);
|
||||||
|
EXPECT_EQ(formatDateValue(std::numeric_limits<time_t>::min() - 1),
|
||||||
|
llvm::None);
|
||||||
|
|
||||||
|
// Can't add the macOS epoch to the converted date_value (the add overflows).
|
||||||
|
EXPECT_EQ(formatDateValue(std::numeric_limits<time_t>::max()), llvm::None);
|
||||||
|
EXPECT_EQ(formatDateValue(std::numeric_limits<time_t>::min()), llvm::None);
|
||||||
|
|
||||||
|
EXPECT_EQ(*formatDateValue(0), "2001-01-01 00:00:00 UTC");
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user