diff --git a/core/base/CMakeLists.txt b/core/base/CMakeLists.txt index 0ce55d1ff4a27..c78f15eed82d1 100644 --- a/core/base/CMakeLists.txt +++ b/core/base/CMakeLists.txt @@ -16,6 +16,7 @@ if(MSVC AND MSVC_VERSION GREATER_EQUAL 1925 AND MSVC_VERSION LESS 1929) endif() set(BASE_HEADERS + ROOT/RCryptoRandom.hxx ROOT/RFloat16.hxx ROOT/TErrorDefaultHandler.hxx ROOT/TExecutorCRTP.hxx @@ -118,6 +119,7 @@ set(BASE_HEADERS set(BASE_SOURCES src/Match.cxx + src/RCryptoRandom.cxx src/String.cxx src/Stringio.cxx src/TApplication.cxx @@ -288,3 +290,70 @@ generateManual(rootclingMan ${CMAKE_SOURCE_DIR}/core/dictgen/src/rootcling-argpa endif() ROOT_ADD_TEST_SUBDIRECTORY(test) + +if(UNIX) + # Determine cryptographic random number generator + + CHECK_CXX_SOURCE_COMPILES("#include + int main() { char buf[32]; arc4random_buf(buf, 32); return 0;}" found_arc4) + + if(found_arc4) + message(STATUS "Found arc4random_buf in stdlib.h") + target_compile_definitions(Core PRIVATE R__ARC4_STDLIB) + else() + set(OLD_CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES}) + set(OLD_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + if(DEFINED LIBBSDROOT) + set(CMAKE_REQUIRED_INCLUDES ${LIBBSDROOT}/include) + set(CMAKE_REQUIRED_LIBRARIES ${LIBBSDROOT}/lib/libbsd.so) + endif() + CHECK_CXX_SOURCE_COMPILES("#include + int main() { char buf[32]; arc4random_buf(buf, 32); return 0;}" found_arc4_bsd) + set(CMAKE_REQUIRED_INCLUDES ${OLD_CMAKE_REQUIRED_INCLUDES}) + set(CMAKE_REQUIRED_LIBRARIES ${OLD_CMAKE_REQUIRED_LIBRARIES}) + if(found_arc4_bsd) + message(STATUS "Found arc4random_buf in bsd/stdlib.h") + target_compile_definitions(Core PRIVATE R__ARC4_BSDLIB) + if(DEFINED LIBBSDROOT) + target_include_directories(Core PRIVATE ${LIBBSDROOT}/include) + target_link_libraries(Core PRIVATE ${LIBBSDROOT}/lib/libbsd.so) + endif() + else() + CHECK_CXX_SOURCE_COMPILES("#include + int main() { char buf[32]; int res = getrandom(buf, 32, GRND_NONBLOCK); return 0;}" found_getrandom) + if(found_getrandom) + message(STATUS "Found getrandom in sys/random.h") + target_compile_definitions(Core PRIVATE R__GETRANDOM_CLIB) + else() + CHECK_CXX_SOURCE_RUNS(" + #include + + int main() { + std::ifstream urandom{\"/dev/urandom\"}; + if (!urandom) { + // This will make the CMake command fail + return 1; + } + + constexpr int len{32}; + char buf[len]; + for (int n = 0; n < len; n++) buf[n] = 0; + urandom.read(buf, len); + + int nmatch = 0; + for (int n = 0; n < len; n++) + if (buf[n] == 0) nmatch++; + + // Fail if no values have changed + return nmatch != len ? 0 : 1; + }" found_urandom) + if(found_urandom) + message(STATUS "Found random device in /dev/urandom") + target_compile_definitions(Core PRIVATE R__USE_URANDOM) + else() + message(FATAL_ERROR "Fail to detect cryptographic random generator") + endif() + endif() + endif() + endif() +endif(UNIX) diff --git a/core/base/inc/ROOT/RCryptoRandom.hxx b/core/base/inc/ROOT/RCryptoRandom.hxx new file mode 100644 index 0000000000000..b028a45951238 --- /dev/null +++ b/core/base/inc/ROOT/RCryptoRandom.hxx @@ -0,0 +1,27 @@ +/// \file ROOT/RCryptoRandom.hxx +/// \ingroup Base +/// \date 2026-04-24 + +/************************************************************************* + * Copyright (C) 1995-2026, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +#ifndef ROOT_RCryptoRandom +#define ROOT_RCryptoRandom + +#include + +namespace ROOT { +namespace Internal { + +/// Get random bytes from the operating system's cryptographic random number generator +bool GetCryptoRandom(void *buf, std::size_t len); + +} // namespace Internal +} // namespace ROOT + +#endif diff --git a/core/base/inc/TDirectory.h b/core/base/inc/TDirectory.h index a95e91d236111..744848ae72948 100644 --- a/core/base/inc/TDirectory.h +++ b/core/base/inc/TDirectory.h @@ -140,7 +140,7 @@ can be replaced with the simpler and exception safe: TObject *fMother{nullptr}; ///< pointer to mother of the directory TList *fList{nullptr}; ///< List of objects in memory - TUUID fUUID; ///< Unique identifier + TUUID fUUID{TUUID::UUIDv4()}; ///< Unique identifier mutable TString fPathBuffer; /// +#else +#if defined(R__ARC4_STDLIB) +#include +#elif defined(R__ARC4_BSDLIB) +#include +#elif defined(R__GETRANDOM_CLIB) +#include +#elif defined(R__USE_URANDOM) +#include +#endif +#endif + +bool ROOT::Internal::GetCryptoRandom(void *buf, std::size_t len) +{ +#ifdef WIN32 + + auto rv = BCryptGenRandom((BCRYPT_ALG_HANDLE)NULL, (PUCHAR)buf, (ULONG)len, BCRYPT_USE_SYSTEM_PREFERRED_RNG); + return rv == STATUS_SUCCESS; + +#else // UNIX + +#if defined(R__ARC4_STDLIB) || defined(R__ARC4_BSDLIB) + arc4random_buf(buf, len); + return true; +#elif defined(R__GETRANDOM_CLIB) + return getrandom(buf, len, GRND_NONBLOCK) == len; +#elif defined(R__USE_URANDOM) + std::ifstream urandom{"/dev/urandom"}; + if (!urandom) + return false; + urandom.read(reinterpret_cast(buf), len); + return true; +#else +#error "Reliable cryptographic random function not defined" + return false; +#endif + +#endif +} diff --git a/core/base/src/TSystem.cxx b/core/base/src/TSystem.cxx index 2585dd507270d..f9811bdc34643 100644 --- a/core/base/src/TSystem.cxx +++ b/core/base/src/TSystem.cxx @@ -22,6 +22,7 @@ allows a simple partial implementation for new OS'es. */ #include +#include #include "strlcpy.h" #include "TSystem.h" #include "TApplication.h" @@ -261,10 +262,13 @@ const char *TSystem::GetError() /// Fill provided buffer with random values /// Returns number of bytes written to buffer or -1 in case of error -Int_t TSystem::GetCryptoRandom(void * /* buf */, Int_t /* len */) +Int_t TSystem::GetCryptoRandom(void *buf, Int_t len) { - Error("GetCryptoRandom", "Not implemented"); - return -1; + if (len < 0) { + return false; + } + const auto rv = ROOT::Internal::GetCryptoRandom(buf, len); + return rv ? len : -1; } diff --git a/core/base/src/TUUID.cxx b/core/base/src/TUUID.cxx index 14bcfb648f6a5..37c532676d9fd 100644 --- a/core/base/src/TUUID.cxx +++ b/core/base/src/TUUID.cxx @@ -113,6 +113,8 @@ system clock tick, the UUID generator will stall until the system clock catches up. */ +#include + #include "TROOT.h" #include "TDatime.h" #include "TUUID.h" @@ -124,6 +126,7 @@ system clock catches up. #include "Bytes.h" #include "TVirtualMutex.h" #include "ThreadLocalStorage.h" +#include #include #include #ifdef R__WIN32 @@ -145,6 +148,28 @@ system clock catches up. #endif #include +//////////////////////////////////////////////////////////////////////////////// +/// Create a v4 UUID. + +TUUID TUUID::UUIDv4() +{ + TUUID uuid{TIndeterminedMarker()}; + + // Ensure we can treat the memory starting at uuid.fTimeLow as an array of 16 octets + assert(&uuid.fNode[5] - reinterpret_cast(&uuid.fTimeLow) + 1 == 16); + + const auto rv = ROOT::Internal::GetCryptoRandom(&uuid.fTimeLow, 16); + R__ASSERT(rv); + // Fix up variant + uuid.fClockSeqHiAndReserved = (uuid.fClockSeqHiAndReserved & 0x3F) | (2 << 6); + // Fix up version + uuid.fTimeHiAndVersion = (uuid.fTimeHiAndVersion & 0x0FFF) | (4 << 12); + + // TODO(jblomer): we do what the default constructor does but is this still used? Can it be deprecated? + uuid.fUUIDIndex = 1 << 30; + + return uuid; +} //////////////////////////////////////////////////////////////////////////////// /// Create a UUID. @@ -569,7 +594,7 @@ void TUUID::Print() const const char *TUUID::AsString() const { - static char uuid[40]; + TTHREAD_TLS(char) uuid[40]; snprintf(uuid,40, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", fTimeLow, fTimeMid, fTimeHiAndVersion, fClockSeqHiAndReserved, diff --git a/core/base/test/CMakeLists.txt b/core/base/test/CMakeLists.txt index ecc8e6161e57b..71787ae28a42e 100644 --- a/core/base/test/CMakeLists.txt +++ b/core/base/test/CMakeLists.txt @@ -26,6 +26,8 @@ ROOT_ADD_GTEST(CoreErrorTests TErrorTests.cxx LIBRARIES Core) ROOT_ADD_GTEST(CoreSystemTests TSystemTests.cxx LIBRARIES Core) +ROOT_ADD_GTEST(CoreCryptoRandomTest CryptoRandomTest.cxx LIBRARIES Core) + ROOT_ADD_GTEST(CoreColorTests TColorTests.cxx LIBRARIES Core) if(CMAKE_SYSTEM_NAME MATCHES "Darwin" AND CMAKE_HOST_SYSTEM_VERSION VERSION_GREATER_EQUAL 25.4) set_tests_properties(gtest-core-base-CoreColorTests PROPERTIES DISABLED True) @@ -49,3 +51,5 @@ endif() configure_file(Foo.C Foo.C COPYONLY) ROOT_ADD_GTEST(IncludePathTest IncludePathTest.cxx LIBRARIES Core) + +ROOT_ADD_GTEST(UUIDTest UUIDTest.cxx LIBRARIES Core) diff --git a/core/base/test/CryptoRandomTest.cxx b/core/base/test/CryptoRandomTest.cxx new file mode 100644 index 0000000000000..a811b6b9ab336 --- /dev/null +++ b/core/base/test/CryptoRandomTest.cxx @@ -0,0 +1,39 @@ +#include "gtest/gtest.h" + +#include + +TEST(TSystem, CryptoRandom) +{ + // test with 512 bits, longer keys may not work + + const int len = 64; + uint8_t buf[64]; + + for (int n = 0; n < len; n++) + buf[n] = 0; + + EXPECT_TRUE(ROOT::Internal::GetCryptoRandom(buf, len)); + + int nmatch = 0; + + for (int n = 0; n < len; n++) + if (buf[n] == 0) + nmatch++; + + // check that values in buffer changed + EXPECT_TRUE(nmatch != len); + + for (int n = 0; n < len; n++) + buf[n] = n; + + EXPECT_TRUE(ROOT::Internal::GetCryptoRandom(buf, len)); + + nmatch = 0; + + for (int n = 0; n < len; n++) + if (buf[n] == n) + nmatch++; + + // check that values in buffer changed + EXPECT_TRUE(nmatch != len); +} diff --git a/core/base/test/TSystemTests.cxx b/core/base/test/TSystemTests.cxx index 03d87fab297f3..b33ddbf253fde 100644 --- a/core/base/test/TSystemTests.cxx +++ b/core/base/test/TSystemTests.cxx @@ -56,43 +56,3 @@ TEST(TSystem, TempFileSuffix) gSystem->Unlink(fname); } - -TEST(TSystem, CryptoRandom) -{ - // test with 512 bits, longer keys may not work - - const int len = 64; - uint8_t buf[64]; - - for (int n = 0; n < len; n++) - buf[n] = 0; - - auto res = gSystem->GetCryptoRandom(buf, len); - - EXPECT_EQ(res, len); - - int nmatch = 0; - - for (int n = 0; n < len; n++) - if (buf[n] == 0) - nmatch++; - - // check that values in buffer changed - EXPECT_TRUE(nmatch != len); - - for (int n = 0; n < len; n++) - buf[n] = n; - - res = gSystem->GetCryptoRandom(buf, len); - - EXPECT_EQ(res, len); - - nmatch = 0; - - for (int n = 0; n < len; n++) - if (buf[n] == n) - nmatch++; - - // check that values in buffer changed - EXPECT_TRUE(nmatch != len); -} diff --git a/core/base/test/UUIDTest.cxx b/core/base/test/UUIDTest.cxx new file mode 100644 index 0000000000000..dea3700052bac --- /dev/null +++ b/core/base/test/UUIDTest.cxx @@ -0,0 +1,22 @@ +#include "gtest/gtest.h" + +#include "TUUID.h" + +#include +#include + +TEST(TUUID, UUIDv4) +{ + std::set uuids; + for (int i = 0; i < 10000; ++i) { + uuids.insert(TUUID::UUIDv4()); + } + EXPECT_EQ(10000u, uuids.size()); + + TUUID u; + EXPECT_EQ('1', u.AsString()[14]); + u = TUUID::UUIDv4(); + std::string str = u.AsString(); + EXPECT_EQ('4', str[14]); + EXPECT_TRUE(str[19] == '8' || str[19] == '9' || str[19] == 'a' || str[19] == 'b'); +} diff --git a/core/unix/CMakeLists.txt b/core/unix/CMakeLists.txt index 9f76985a8c54a..f1bfed48174af 100644 --- a/core/unix/CMakeLists.txt +++ b/core/unix/CMakeLists.txt @@ -20,68 +20,4 @@ if (CMAKE_SYSTEM_NAME MATCHES FreeBSD) target_link_libraries(Core PRIVATE execinfo util) endif() -CHECK_CXX_SOURCE_COMPILES("#include -int main() { char buf[32]; arc4random_buf(buf, 32); return 0;}" found_arc4) - -if(found_arc4) - message(STATUS "Found arc4random_buf in stdlib.h") - target_compile_definitions(Core PRIVATE R__ARC4_STDLIB) -else() - set(OLD_CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES}) - set(OLD_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) - if(DEFINED LIBBSDROOT) - set(CMAKE_REQUIRED_INCLUDES ${LIBBSDROOT}/include) - set(CMAKE_REQUIRED_LIBRARIES ${LIBBSDROOT}/lib/libbsd.so) - endif() - CHECK_CXX_SOURCE_COMPILES("#include - int main() { char buf[32]; arc4random_buf(buf, 32); return 0;}" found_arc4_bsd) - set(CMAKE_REQUIRED_INCLUDES ${OLD_CMAKE_REQUIRED_INCLUDES}) - set(CMAKE_REQUIRED_LIBRARIES ${OLD_CMAKE_REQUIRED_LIBRARIES}) - if(found_arc4_bsd) - message(STATUS "Found arc4random_buf in bsd/stdlib.h") - target_compile_definitions(Core PRIVATE R__ARC4_BSDLIB) - if(DEFINED LIBBSDROOT) - target_include_directories(Core PRIVATE ${LIBBSDROOT}/include) - target_link_libraries(Core PRIVATE ${LIBBSDROOT}/lib/libbsd.so) - endif() - else() - CHECK_CXX_SOURCE_COMPILES("#include - int main() { char buf[32]; int res = getrandom(buf, 32, GRND_NONBLOCK); return 0;}" found_getrandom) - if(found_getrandom) - message(STATUS "Found getrandom in sys/random.h") - target_compile_definitions(Core PRIVATE R__GETRANDOM_CLIB) - else() - CHECK_CXX_SOURCE_RUNS(" - #include - - int main() { - std::ifstream urandom{\"/dev/urandom\"}; - if (!urandom) { - // This will make the CMake command fail - return 1; - } - - constexpr int len{32}; - char buf[len]; - for (int n = 0; n < len; n++) buf[n] = 0; - urandom.read(buf, len); - - int nmatch = 0; - for (int n = 0; n < len; n++) - if (buf[n] == 0) nmatch++; - - // Fail if no values have changed - return nmatch != len ? 0 : 1; - }" found_urandom) - if(found_urandom) - message(STATUS "Found random device in /dev/urandom") - target_compile_definitions(Core PRIVATE R__USE_URANDOM) - else() - message(FATAL_ERROR "Fail to detect cryptographic random generator") - endif() - endif() - endif() -endif() - - ROOT_INSTALL_HEADERS() diff --git a/core/unix/inc/TUnixSystem.h b/core/unix/inc/TUnixSystem.h index 9f89288d96de1..8d92bd4597a85 100644 --- a/core/unix/inc/TUnixSystem.h +++ b/core/unix/inc/TUnixSystem.h @@ -78,7 +78,6 @@ class TUnixSystem : public TSystem { void SetProgname(const char *name) override; void SetDisplay() override; const char *GetError() override; - Int_t GetCryptoRandom(void *buf, Int_t len) override; const char *HostName() override; //---- EventLoop -------------------------------------------- diff --git a/core/unix/src/TUnixSystem.cxx b/core/unix/src/TUnixSystem.cxx index 6ae409145d3d3..17efe4c182da5 100644 --- a/core/unix/src/TUnixSystem.cxx +++ b/core/unix/src/TUnixSystem.cxx @@ -731,30 +731,6 @@ const char *TUnixSystem::GetError() #endif } -//////////////////////////////////////////////////////////////////////////////// -/// Return cryptographic random number -/// Fill provided buffer with random values -/// Returns number of bytes written to buffer or -1 in case of error - -Int_t TUnixSystem::GetCryptoRandom(void *buf, Int_t len) -{ -#if defined(R__ARC4_STDLIB) || defined(R__ARC4_BSDLIB) - arc4random_buf(buf, len); - return len; -#elif defined(R__GETRANDOM_CLIB) - return getrandom(buf, len, GRND_NONBLOCK); -#elif defined(R__USE_URANDOM) - std::ifstream urandom{"/dev/urandom"}; - if (!urandom) - return -1; - urandom.read(reinterpret_cast(buf), len); - return len; -#else -#error "Reliable cryptographic random function not defined" - return -1; -#endif -} - //////////////////////////////////////////////////////////////////////////////// /// Return the system's host name. @@ -4298,7 +4274,7 @@ int TUnixSystem::UnixUnixConnect(const char *sockpath) /// Use tcpwindowsize to specify the size of the receive buffer, it has /// to be specified here to make sure the window scale option is set (for /// tcpwindowsize > 65KB and for platforms supporting window scaling). -/// The socketBindOption parameter allows to specify how the socket will be +/// The socketBindOption parameter allows to specify how the socket will be /// bound. See the documentation of ESocketBindOption for the details. /// Returns socket fd or -1 if socket() failed, -2 if bind() failed /// or -3 if listen() failed. @@ -4378,7 +4354,7 @@ int TUnixSystem::UnixTcpService(int port, Bool_t reuse, int backlog, /// how many sockets can be waiting to be accepted. If port is 0 a port /// scan will be done to find a free port. This option is mutual exlusive /// with the reuse option. -/// The socketBindOption parameter allows to specify how the socket will be +/// The socketBindOption parameter allows to specify how the socket will be /// bound. See the documentation of ESocketBindOption for the details. int TUnixSystem::UnixUdpService(int port, int backlog, ESocketBindOption socketBindOption) @@ -5268,10 +5244,10 @@ static void GetLinuxMemInfo(MemInfo_t *meminfo) /* * Compute memory partition like procps(free), see https://gitlab.com/procps-ng/procps/-/blob/master/proc/sysinfo.c - * + * * fMemShared is a part of Cached (see https://lore.kernel.org/patchwork/patch/648763/), does not subtract twice from used */ - + meminfo->fMemCached = meminfo->fMemCached + meminfo->fSReclaimable - meminfo->fMemShared; const Int_t usedDiff = meminfo->fMemFree + meminfo->fMemCached + meminfo->fSReclaimable + meminfo->fMemBuffer; diff --git a/core/winnt/inc/TWinNTSystem.h b/core/winnt/inc/TWinNTSystem.h index c4fe560ebff12..91092a4541604 100644 --- a/core/winnt/inc/TWinNTSystem.h +++ b/core/winnt/inc/TWinNTSystem.h @@ -101,7 +101,6 @@ class TWinNTSystem : public TSystem { const char *BaseName(const char *name) override; void SetProgname(const char *name) override; const char *GetError() override; - Int_t GetCryptoRandom(void *buf, Int_t len) override; const char *HostName() override; void *GetGUIThreadHandle() const {return fGUIThreadHandle;} ULong_t GetGUIThreadId() const {return fGUIThreadId;} diff --git a/core/winnt/src/TWinNTSystem.cxx b/core/winnt/src/TWinNTSystem.cxx index 42d8329ba1970..98d10a5319893 100644 --- a/core/winnt/src/TWinNTSystem.cxx +++ b/core/winnt/src/TWinNTSystem.cxx @@ -1266,17 +1266,6 @@ const char *TWinNTSystem::GetError() return sys_errlist[err]; } -//////////////////////////////////////////////////////////////////////////////// -/// Return cryptographic random number -/// Fill provided buffer with random values -/// Returns number of bytes written to buffer or -1 in case of error - -Int_t TWinNTSystem::GetCryptoRandom(void *buf, Int_t len) -{ - auto res = BCryptGenRandom((BCRYPT_ALG_HANDLE) NULL, (PUCHAR) buf, (ULONG) len, BCRYPT_USE_SYSTEM_PREFERRED_RNG); - return !res ? len : -1; -} - //////////////////////////////////////////////////////////////////////////////// /// Return the system's host name. @@ -5384,7 +5373,7 @@ int TWinNTSystem::OpenConnection(const char *server, int port, int tcpwindowsize /// Use tcpwindowsize to specify the size of the receive buffer, it has /// to be specified here to make sure the window scale option is set (for /// tcpwindowsize > 65KB and for platforms supporting window scaling). -/// The socketBindOption parameter allows to specify how the socket will be +/// The socketBindOption parameter allows to specify how the socket will be /// bound. See the documentation of ESocketBindOption for the details. /// Returns socket fd or -1 if socket() failed, -2 if bind() failed /// or -3 if listen() failed. @@ -5474,7 +5463,7 @@ int TWinNTSystem::AnnounceUdpService(int port, int backlog, ESocketBindOption so // how many sockets can be waiting to be accepted. If port is 0 a port // scan will be done to find a free port. This option is mutual exlusive // with the reuse option. - // The socketBindOption parameter allows to specify how the socket will be + // The socketBindOption parameter allows to specify how the socket will be // bound. See the documentation of ESocketBindOption for the details. const short kSOCKET_MINPORT = 5000, kSOCKET_MAXPORT = 15000; diff --git a/io/io/test/TFileTests.cxx b/io/io/test/TFileTests.cxx index 20dc74fb6b2e4..5ef2ed684feee 100644 --- a/io/io/test/TFileTests.cxx +++ b/io/io/test/TFileTests.cxx @@ -327,3 +327,8 @@ TEST(TFile, PersistTObjectStdArray) gSystem->Unlink(filename); } +TEST(TFile, UUID) +{ + TMemFile f("uuidtest.root", "RECREATE"); + EXPECT_EQ('4', f.GetUUID().AsString()[14]); +} diff --git a/tree/ntuple/src/RMiniFile.cxx b/tree/ntuple/src/RMiniFile.cxx index be81fde06569a..ce71ec7e203a6 100644 --- a/tree/ntuple/src/RMiniFile.cxx +++ b/tree/ntuple/src/RMiniFile.cxx @@ -531,7 +531,7 @@ struct RTFUUID { RTFUUID() { - TUUID uuid; + TUUID uuid{TUUID::UUIDv4()}; char *buffer = reinterpret_cast(this); uuid.FillBuffer(buffer); assert(reinterpret_cast(buffer) <= (this + 1)); diff --git a/tree/ntuple/test/ntuple_minifile.cxx b/tree/ntuple/test/ntuple_minifile.cxx index 56e1b427f6edc..6dd2ab7ef4c70 100644 --- a/tree/ntuple/test/ntuple_minifile.cxx +++ b/tree/ntuple/test/ntuple_minifile.cxx @@ -828,6 +828,7 @@ TEST(MiniFile, UUID) TUUID uuid; uuid.SetUUID("00000000-0000-0000-0000-000000000000"); EXPECT_NE(uuid, f->GetUUID()); + EXPECT_EQ('4', f->GetUUID().AsString()[14]); } TEST(MiniFile, FreeSlots)