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/ValueObjectConstResult.h"
|
||||
#include "lldb/DataFormatters/FormattersHelpers.h"
|
||||
#include "lldb/DataFormatters/Mock.h"
|
||||
#include "lldb/DataFormatters/StringPrinter.h"
|
||||
#include "lldb/DataFormatters/TypeSummary.h"
|
||||
#include "lldb/Host/Time.h"
|
||||
@ -27,6 +28,7 @@
|
||||
|
||||
#include "llvm/ADT/APInt.h"
|
||||
#include "llvm/ADT/bit.h"
|
||||
#include "llvm/Support/CheckedArithmetic.h"
|
||||
|
||||
#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
|
||||
|
||||
@ -785,6 +787,34 @@ static double decodeTaggedTimeInterval(uint64_t encodedTimeInterval) {
|
||||
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(
|
||||
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
|
||||
ProcessSP process_sp = valobj.GetProcessSP();
|
||||
@ -828,6 +858,16 @@ bool lldb_private::formatters::NSDateSummaryProvider(
|
||||
if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) {
|
||||
date_value_bits = ((value_bits << 8) | (info_bits << 4));
|
||||
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 {
|
||||
llvm::Triple triple(
|
||||
process_sp->GetTarget().GetArchitecture().GetTriple());
|
||||
@ -850,34 +890,7 @@ bool lldb_private::formatters::NSDateSummaryProvider(
|
||||
} else
|
||||
return false;
|
||||
|
||||
if (date_value == -63114076800) {
|
||||
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;
|
||||
return NSDate::FormatDateValue(date_value, stream);
|
||||
}
|
||||
|
||||
bool lldb_private::formatters::ObjCClassSummaryProvider(
|
||||
|
@ -1,5 +1,6 @@
|
||||
add_lldb_unittest(LLDBFormatterTests
|
||||
FormatManagerTests.cpp
|
||||
MockTests.cpp
|
||||
StringPrinterTests.cpp
|
||||
|
||||
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