blob: ff2933a26073c97437f5cb7a0788b622c56a46fa [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/tracing/common/system_log_event_utils_win.h"
#include <windows.h>
#include "base/containers/span.h"
#include "base/strings/string_view_util.h"
namespace {
template <class CharT>
std::optional<std::basic_string<CharT>> CopyBasicString(
base::BufferIterator<const uint8_t>& iterator) {
auto before = iterator;
// Advance `iterator` forward until a terminator is found.
while (true) {
// Use `CopyObject()` to avoid unaligned reads for multi-byte char strings.
auto optional_char = iterator.CopyObject<CharT>();
if (!optional_char.has_value()) { // Premature end of data.
iterator.Seek(before.position()); // Rewind.
return std::nullopt;
}
if (!*optional_char) {
break; // String terminator found.
}
}
// Compute the string length, in chars, excluding the terminator.
auto size = (iterator.position() - before.position()) / sizeof(CharT) - 1;
return std::basic_string<CharT>(
base::as_string_view(before.Span<CharT>(size)));
}
} // namespace
namespace tracing {
std::optional<std::string> CopyString(
base::BufferIterator<const uint8_t>& iterator) {
return CopyBasicString<char>(iterator);
}
std::optional<std::wstring> CopyWString(
base::BufferIterator<const uint8_t>& iterator) {
return CopyBasicString<wchar_t>(iterator);
}
std::optional<base::win::Sid> CopySid(
size_t pointer_size,
base::BufferIterator<const uint8_t>& iterator) {
// Operate on a copy of the caller's iterator so that the caller's is only
// modified on success.
base::BufferIterator<const uint8_t> temp_iter(iterator);
// Source:
// https://learn.microsoft.com/en-us/windows/win32/etw/event-tracing-mof-qualifiers.
// "If the first 4-bytes (ULONG) of the blob is nonzero, the blob contains a
// SID." If the blob contains a SID, the first two `pointer_size` elements are
// a `TOKEN_USER` struct and the remainder is a `SID` struct. In practice, a
// SID is present even if the first 32-bit int is zero, so ignore its value,
// and skip the `TOKEN_USER` struct (two pointers-sized elements) to reach the
// bytes holding the SID.
if (temp_iter.Object<uint32_t>() == nullptr ||
temp_iter.Span<uint8_t>(2 * pointer_size).empty()) {
return std::nullopt;
}
const auto sid_position = temp_iter.position();
const auto bytes_remaining = temp_iter.total_size() - sid_position;
if (bytes_remaining < 2) {
return std::nullopt;
}
auto remaining_span = temp_iter.Span<uint8_t>(bytes_remaining);
// The second byte in `remaining_span` is the SubAuthorityCount byte. Use it
// to ensure that there is enough data remaining to hold the indicated number
// of subauthorities as per the citation above.
if (bytes_remaining < sizeof(DWORD) * remaining_span[1] + 8) {
return std::nullopt;
}
const SID* sid = reinterpret_cast<const SID*>(remaining_span.data());
if (!::IsValidSid(const_cast<SID*>(sid))) {
return std::nullopt;
}
const auto sid_length = ::GetLengthSid(const_cast<SID*>(sid));
if (sid_length > remaining_span.size()) {
return std::nullopt;
}
// Advance the caller's iterator to just past the sid.
iterator.Seek(sid_position + sid_length);
// Return the SID.
return base::win::Sid::FromPSID(const_cast<SID*>(sid));
}
} // namespace tracing