diff options
Diffstat (limited to 'lldb/tools/debugserver/source/RNBRemote.cpp')
| -rw-r--r-- | lldb/tools/debugserver/source/RNBRemote.cpp | 403 |
1 files changed, 293 insertions, 110 deletions
diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp index d9fb22c6a1c0..b2d79377f1ee 100644 --- a/lldb/tools/debugserver/source/RNBRemote.cpp +++ b/lldb/tools/debugserver/source/RNBRemote.cpp @@ -22,6 +22,9 @@ #include <mach/mach_vm.h> #include <mach/task_info.h> #include <memory> +#if __has_include(<os/security_config.h>) +#include <os/security_config.h> +#endif #include <pwd.h> #include <string> #include <sys/stat.h> @@ -90,11 +93,34 @@ static const std::string JSON_ASYNC_TYPE_KEY_NAME("type"); std::setfill(' ') << std::setw((iword_idx)) << "" #define INDENT_WITH_TABS(iword_idx) \ std::setfill('\t') << std::setw((iword_idx)) << "" -// Class to handle communications via gdb remote protocol. -// Prototypes +// If `ch` is a meta character as per the binary packet convention in the +// gdb-remote protocol, quote it and write it into `stream`, otherwise write it +// as is. +static void binary_encode_char(std::ostringstream &stream, char ch) { + if (ch == '#' || ch == '$' || ch == '}' || ch == '*') { + stream.put('}'); + stream.put(ch ^ 0x20); + } else { + stream.put(ch); + } +} + +// Equivalent to calling binary_encode_char for every element of `data`. +static void binary_encode_data_vector(std::ostringstream &stream, + std::vector<uint8_t> data) { + for (auto ch : data) + binary_encode_char(stream, ch); +} -static std::string binary_encode_string(const std::string &s); +// Quote any meta characters in a std::string as per the binary +// packet convention in the gdb-remote protocol. +static std::string binary_encode_string(const std::string &s) { + std::ostringstream stream; + for (char ch : s) + binary_encode_char(stream, ch); + return stream.str(); +} // Decode a single hex character and return the hex value as a number or // -1 if "ch" is not a hex character. @@ -136,16 +162,30 @@ static std::string decode_hex_ascii_string(const char *p, return arg; } -uint64_t decode_uint64(const char *p, int base, char **end = nullptr, - uint64_t fail_value = 0) { +static uint64_t decode_uint64(const char *p, int base, char **end = nullptr, + uint64_t fail_value = 0) { nub_addr_t addr = strtoull(p, end, 16); if (addr == 0 && errno != 0) return fail_value; return addr; } -void append_hex_value(std::ostream &ostrm, const void *buf, size_t buf_size, - bool swap) { +/// Attempts to parse a prefix of `number_str` as a uint64_t. If +/// successful, the number is returned and the prefix is dropped from +/// `number_str`. +static std::optional<uint64_t> extract_u64(std::string_view &number_str) { + char *str_end = nullptr; + errno = 0; + uint64_t number = strtoull(number_str.data(), &str_end, 16); + if (errno != 0) + return std::nullopt; + assert(str_end); + number_str.remove_prefix(str_end - number_str.data()); + return number; +} + +static void append_hex_value(std::ostream &ostrm, const void *buf, + size_t buf_size, bool swap) { int i; const uint8_t *p = (const uint8_t *)buf; if (swap) { @@ -157,7 +197,7 @@ void append_hex_value(std::ostream &ostrm, const void *buf, size_t buf_size, } } -std::string cstring_to_asciihex_string(const char *str) { +static std::string cstring_to_asciihex_string(const char *str) { std::string hex_str; hex_str.reserve(strlen(str) * 2); while (str && *str) { @@ -169,7 +209,8 @@ std::string cstring_to_asciihex_string(const char *str) { return hex_str; } -void append_hexified_string(std::ostream &ostrm, const std::string &string) { +static void append_hexified_string(std::ostream &ostrm, + const std::string &string) { size_t string_size = string.size(); const char *string_buf = string.c_str(); for (size_t i = 0; i < string_size; i++) { @@ -177,6 +218,25 @@ void append_hexified_string(std::ostream &ostrm, const std::string &string) { } } +/// Returns true if `str` starts with `prefix`. +static bool starts_with(std::string_view str, std::string_view prefix) { + return str.substr(0, prefix.size()) == prefix; +} + +/// Splits `list_str` into multiple string_views separated by `,`. +static std::vector<std::string_view> +parse_comma_separated_list(std::string_view list_str) { + std::vector<std::string_view> list; + while (!list_str.empty()) { + auto pos = list_str.find(','); + list.push_back(list_str.substr(0, pos)); + if (pos == list_str.npos) + break; + list_str.remove_prefix(pos + 1); + } + return list; +} + // from System.framework/Versions/B/PrivateHeaders/sys/codesign.h extern "C" { #define CS_OPS_STATUS 0 /* return status */ @@ -243,6 +303,11 @@ void RNBRemote::CreatePacketTable() { "Read memory")); t.push_back(Packet(read_register, &RNBRemote::HandlePacket_p, NULL, "p", "Read one register")); + // Careful: this *must* come before the `M` packet, as debugserver matches + // packet prefixes against known packet names. Inverting the order would match + // `MultiMemRead` as an `M` packet. + t.push_back(Packet(multi_mem_read, &RNBRemote::HandlePacket_MultiMemRead, + NULL, "MultiMemRead", "Read multiple memory addresses")); t.push_back(Packet(write_memory, &RNBRemote::HandlePacket_M, NULL, "M", "Write memory")); t.push_back(Packet(write_register, &RNBRemote::HandlePacket_P, NULL, "P", @@ -502,6 +567,8 @@ void RNBRemote::CreatePacketTable() { memory_region_info, &RNBRemote::HandlePacket_MemoryRegionInfo, NULL, "qMemoryRegionInfo", "Return size and attributes of a memory region that " "contains the given address")); + t.push_back(Packet(get_memory_tags, &RNBRemote::HandlePacket_qMemTags, NULL, + "qMemTags", "Return tags for a region of memory")); t.push_back(Packet(get_profile_data, &RNBRemote::HandlePacket_GetProfileData, NULL, "qGetProfileData", "Return profiling data of the current target.")); @@ -1022,8 +1089,6 @@ rnb_err_t RNBRemote::HandleAsyncPacket(PacketEnum *type) { rnb_err_t RNBRemote::HandleReceivedPacket(PacketEnum *type) { static DNBTimer g_packetTimer(true); - // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s", - // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); rnb_err_t err = rnb_err; std::string packet_data; RNBRemote::Packet packet_info; @@ -1279,8 +1344,7 @@ static cpu_type_t best_guess_cpu_type() { LEN is the number of bytes to be processed. If a character is escaped, it is 2 characters for LEN. A LEN of -1 means decode-until-nul-byte (end of string). */ - -std::vector<uint8_t> decode_binary_data(const char *str, size_t len) { +static std::vector<uint8_t> decode_binary_data(const char *str, size_t len) { std::vector<uint8_t> bytes; if (len == 0) { return bytes; @@ -1299,31 +1363,10 @@ std::vector<uint8_t> decode_binary_data(const char *str, size_t len) { return bytes; } -// Quote any meta characters in a std::string as per the binary -// packet convention in the gdb-remote protocol. - -static std::string binary_encode_string(const std::string &s) { - std::string output; - const size_t s_size = s.size(); - const char *s_chars = s.c_str(); - - for (size_t i = 0; i < s_size; i++) { - unsigned char ch = *(s_chars + i); - if (ch == '#' || ch == '$' || ch == '}' || ch == '*') { - output.push_back('}'); // 0x7d - output.push_back(ch ^ 0x20); - } else { - output.push_back(ch); - } - } - return output; -} - // If the value side of a key-value pair in JSON is a string, // and that string has a " character in it, the " character must // be escaped. - -std::string json_string_quote_metachars(const std::string &s) { +static std::string json_string_quote_metachars(const std::string &s) { if (s.find('"') == std::string::npos) return s; @@ -1457,15 +1500,6 @@ bool RNBRemote::InitializeRegisters(bool force) { } } - // for (auto ®_entry: g_dynamic_register_map) - // { - // DNBLogThreaded("%4i: size = %3u, pseudo = %i, name = %s", - // reg_entry.offset, - // reg_entry.nub_info.size, - // reg_entry.nub_info.value_regs != NULL, - // reg_entry.nub_info.name); - // } - g_reg_entries = g_dynamic_register_map.data(); g_num_reg_entries = g_dynamic_register_map.size(); } @@ -1714,7 +1748,7 @@ rnb_err_t RNBRemote::HandlePacket_qThreadExtraInfo(const char *p) { return SendPacket(""); } -const char *k_space_delimiters = " \t"; +static const char *k_space_delimiters = " \t"; static void skip_spaces(std::string &line) { if (!line.empty()) { size_t space_pos = line.find_first_not_of(k_space_delimiters); @@ -2019,7 +2053,7 @@ rnb_err_t RNBRemote::HandlePacket_qRegisterInfo(const char *p) { QSetLogging:bitmask=LOG_ALL;mode=asl; */ -rnb_err_t set_logging(const char *p) { +static rnb_err_t set_logging(const char *p) { int bitmask = 0; while (p && *p != '\0') { if (strncmp(p, "bitmask=", sizeof("bitmask=") - 1) == 0) { @@ -2563,11 +2597,10 @@ rnb_err_t RNBRemote::HandlePacket_QSetProcessEvent(const char *p) { // If a fail_value is provided, a correct-length reply is always provided, // even if the register cannot be read right now on this thread. -bool register_value_in_hex_fixed_width(std::ostream &ostrm, nub_process_t pid, - nub_thread_t tid, - const register_map_entry_t *reg, - const DNBRegisterValue *reg_value_ptr, - std::optional<uint8_t> fail_value) { +static bool register_value_in_hex_fixed_width( + std::ostream &ostrm, nub_process_t pid, nub_thread_t tid, + const register_map_entry_t *reg, const DNBRegisterValue *reg_value_ptr, + std::optional<uint8_t> fail_value) { if (reg != NULL) { std::unique_ptr<DNBRegisterValue> reg_value = std::make_unique<DNBRegisterValue>(); @@ -2594,7 +2627,7 @@ bool register_value_in_hex_fixed_width(std::ostream &ostrm, nub_process_t pid, return false; } -void debugserver_regnum_with_fixed_width_hex_register_value( +static void debugserver_regnum_with_fixed_width_hex_register_value( std::ostream &ostrm, nub_process_t pid, nub_thread_t tid, const register_map_entry_t *reg, const DNBRegisterValue *reg_value_ptr, std::optional<uint8_t> fail_value) { @@ -3155,6 +3188,88 @@ rnb_err_t RNBRemote::HandlePacket_m(const char *p) { return SendPacket(ostrm.str()); } +rnb_err_t RNBRemote::HandlePacket_MultiMemRead(const char *p) { + const std::string_view packet_name("MultiMemRead:"); + std::string_view packet(p); + + if (!starts_with(packet, packet_name)) + return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, + "Invalid MultiMemRead packet prefix"); + + packet.remove_prefix(packet_name.size()); + + const std::string_view ranges_prefix("ranges:"); + if (!starts_with(packet, ranges_prefix)) + return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(), + "Missing 'ranges' in MultiMemRead packet"); + packet.remove_prefix(ranges_prefix.size()); + + std::vector<std::pair<nub_addr_t, std::size_t>> ranges; + std::size_t total_length = 0; + + // Ranges should have the form: <addr>,<size>[,<addr>,<size>]*; + auto end_of_ranges_pos = packet.find(';'); + if (end_of_ranges_pos == packet.npos) + return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(), + "MultiMemRead missing end of ranges marker"); + + std::vector<std::string_view> numbers_list = + parse_comma_separated_list(packet.substr(0, end_of_ranges_pos)); + packet.remove_prefix(end_of_ranges_pos + 1); + + // Ranges are pairs, so the number of elements must be even. + if (numbers_list.size() % 2 == 1) + return HandlePacket_ILLFORMED( + __FILE__, __LINE__, p, + "MultiMemRead has an odd number of numbers for the ranges"); + + for (unsigned idx = 0; idx < numbers_list.size(); idx += 2) { + std::optional<uint64_t> maybe_addr = extract_u64(numbers_list[idx]); + std::optional<uint64_t> maybe_length = extract_u64(numbers_list[idx + 1]); + if (!maybe_addr || !maybe_length) + return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(), + "Invalid MultiMemRead range"); + // A sanity check that the packet requested is not too large or a negative + // number. + if (*maybe_length > 4 * 1024 * 1024) + return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(), + "MultiMemRead length is too large"); + + ranges.emplace_back(*maybe_addr, *maybe_length); + total_length += *maybe_length; + } + + if (ranges.empty()) + return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, + "MultiMemRead has an empty range list"); + + if (!packet.empty()) + return HandlePacket_ILLFORMED( + __FILE__, __LINE__, p, "MultiMemRead packet has unrecognized fields"); + + std::vector<std::vector<uint8_t>> buffers; + buffers.reserve(ranges.size()); + for (auto [base_addr, length] : ranges) { + buffers.emplace_back(length, 0); + nub_size_t bytes_read = DNBProcessMemoryRead(m_ctx.ProcessID(), base_addr, + length, buffers.back().data()); + buffers.back().resize(bytes_read); + } + + std::ostringstream reply_stream; + bool first = true; + for (const std::vector<uint8_t> &buffer : buffers) { + reply_stream << (first ? "" : ",") << std::hex << buffer.size(); + first = false; + } + reply_stream << ';'; + + for (const std::vector<uint8_t> &buffer : buffers) + binary_encode_data_vector(reply_stream, buffer); + + return SendPacket(reply_stream.str()); +} + // Read memory, sent it up as binary data. // Usage: xADDR,LEN // ADDR and LEN are both base 16. @@ -3211,21 +3326,9 @@ rnb_err_t RNBRemote::HandlePacket_x(const char *p) { return SendErrorPacket("E80"); } - std::vector<uint8_t> buf_quoted; - buf_quoted.reserve(bytes_read + 30); - for (nub_size_t i = 0; i < bytes_read; i++) { - if (buf[i] == '#' || buf[i] == '$' || buf[i] == '}' || buf[i] == '*') { - buf_quoted.push_back(0x7d); - buf_quoted.push_back(buf[i] ^ 0x20); - } else { - buf_quoted.push_back(buf[i]); - } - } - length = buf_quoted.size(); - + buf.resize(bytes_read); std::ostringstream ostrm; - for (unsigned long i = 0; i < length; i++) - ostrm << buf_quoted[i]; + binary_encode_data_vector(ostrm, buf); return SendPacket(ostrm.str()); } @@ -3475,6 +3578,18 @@ static bool GetProcessNameFrom_vAttach(const char *&p, return return_val; } +static bool supports_memory_tagging() { + const char *name = "hw.optional.arm.FEAT_MTE4"; + uint32_t val; + size_t len = sizeof(val); + int ret = ::sysctlbyname(name, &val, &len, nullptr, 0); + if (ret != 0) + return false; + + assert(len == sizeof(val)); + return val; +} + rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) { uint32_t max_packet_size = 128 * 1024; // 128 KiB is a reasonable max packet // size--debugger can always use less @@ -3505,6 +3620,10 @@ rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) { reply << "SupportedWatchpointTypes=x86_64;"; #endif + if (supports_memory_tagging()) + reply << "memory-tagging+;"; + + reply << "MultiMemRead+;"; return SendPacket(reply.str().c_str()); } @@ -4251,7 +4370,6 @@ rnb_err_t RNBRemote::HandlePacket_MemoryRegionInfo(const char *p) { is in unmapped memory Region lookup cannot be performed on this platform or process is not yet launched - This packet isn't implemented Examples of use: qMemoryRegionInfo:3a55140 @@ -4303,6 +4421,16 @@ rnb_err_t RNBRemote::HandlePacket_MemoryRegionInfo(const char *p) { ostrm << 'x'; ostrm << ';'; + if (!region_info.flags.empty()) { + ostrm << "flags:"; + for (size_t i = 0; i < region_info.flags.size(); i++) { + if (i != 0) + ostrm << " "; // Separator is whitespace + ostrm << region_info.flags[i]; + } + ostrm << ";"; + } + ostrm << "dirty-pages:"; if (region_info.dirty_pages.size() > 0) { bool first = true; @@ -4327,6 +4455,62 @@ rnb_err_t RNBRemote::HandlePacket_MemoryRegionInfo(const char *p) { return SendPacket(ostrm.str()); } +// qMemTags:<hex address>,<hex length>:<hex type> +rnb_err_t RNBRemote::HandlePacket_qMemTags(const char *p) { + nub_process_t pid = m_ctx.ProcessID(); + if (pid == INVALID_NUB_PROCESS) + return SendPacket("OK"); + + StdStringExtractor packet(p); + packet.SetFilePos(strlen("qMemTags:")); + + // Address + nub_addr_t addr = + packet.GetHexMaxU64(StdStringExtractor::BigEndian, INVALID_NUB_ADDRESS); + if (addr == INVALID_NUB_ADDRESS) + return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, + "Invalid/missing address in qMemTags packet"); + // , + if (packet.GetChar() != ',') + return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, + "Invalid qMemTags packet format"); + // Length + uint64_t length = packet.GetHexMaxU64(StdStringExtractor::BigEndian, 0); + if (length == 0) + return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, + "Invalid/missing length in qMemTags packet"); + // : + if (packet.GetChar() != ':') + return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, + "Invalid qMemTags packet format"); + // Type + // On the LLDB side this is a `int32_t` serialized as (unsigned) hex, which + // means negative values will show up as large positive values here. Right + // now, we only support MTE (type 1), so we can ignore this complication. + uint32_t type = packet.GetHexMaxU32(StdStringExtractor::BigEndian, 0); + if (type != 1 /* MTE */) + return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, + "Invalid/missing type in qMemTags packet, " + "only MTE (type 1) is supported"); + // <EOF> + if (packet.GetBytesLeft() != 0) + return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, + "Invalid qMemTags packet format"); + + std::vector<uint8_t> tags; + bool ok = DNBProcessGetMemoryTags(pid, addr, length, tags); + if (!ok) + return SendErrorPacket("E91"); + + std::ostringstream ostrm; + ostrm << "m"; // Multi part replies + for (uint8_t tag : tags) { + ostrm << RAWHEX8(tag); // 2 hex chars per tag + } + + return SendPacket(ostrm.str()); +} + // qGetProfileData;scan_type:0xYYYYYYY rnb_err_t RNBRemote::HandlePacket_GetProfileData(const char *p) { nub_process_t pid = m_ctx.ProcessID(); @@ -4817,8 +5001,8 @@ rnb_err_t RNBRemote::HandlePacket_qHostInfo(const char *p) { return SendPacket(strm.str()); } -void XMLElementStart(std::ostringstream &s, uint32_t indent, const char *name, - bool has_attributes) { +static void XMLElementStart(std::ostringstream &s, uint32_t indent, + const char *name, bool has_attributes) { if (indent) s << INDENT_WITH_SPACES(indent); s << '<' << name; @@ -4826,43 +5010,22 @@ void XMLElementStart(std::ostringstream &s, uint32_t indent, const char *name, s << '>' << std::endl; } -void XMLElementStartEndAttributes(std::ostringstream &s, bool empty) { +static void XMLElementStartEndAttributes(std::ostringstream &s, bool empty) { if (empty) s << '/'; s << '>' << std::endl; } -void XMLElementEnd(std::ostringstream &s, uint32_t indent, const char *name) { +static void XMLElementEnd(std::ostringstream &s, uint32_t indent, + const char *name) { if (indent) s << INDENT_WITH_SPACES(indent); s << '<' << '/' << name << '>' << std::endl; } -void XMLElementWithStringValue(std::ostringstream &s, uint32_t indent, - const char *name, const char *value, - bool close = true) { - if (value) { - if (indent) - s << INDENT_WITH_SPACES(indent); - s << '<' << name << '>' << value; - if (close) - XMLElementEnd(s, 0, name); - } -} - -void XMLElementWithUnsignedValue(std::ostringstream &s, uint32_t indent, - const char *name, uint64_t value, - bool close = true) { - if (indent) - s << INDENT_WITH_SPACES(indent); - - s << '<' << name << '>' << DECIMAL << value; - if (close) - XMLElementEnd(s, 0, name); -} - -void XMLAttributeString(std::ostringstream &s, const char *name, - const char *value, const char *default_value = NULL) { +static void XMLAttributeString(std::ostringstream &s, const char *name, + const char *value, + const char *default_value = NULL) { if (value) { if (default_value && strcmp(value, default_value) == 0) return; // No need to emit the attribute because it matches the default @@ -4871,15 +5034,16 @@ void XMLAttributeString(std::ostringstream &s, const char *name, } } -void XMLAttributeUnsignedDecimal(std::ostringstream &s, const char *name, - uint64_t value) { +static void XMLAttributeUnsignedDecimal(std::ostringstream &s, const char *name, + uint64_t value) { s << ' ' << name << "=\"" << DECIMAL << value << "\""; } -void GenerateTargetXMLRegister(std::ostringstream &s, const uint32_t reg_num, - nub_size_t num_reg_sets, - const DNBRegisterSetInfo *reg_set_info, - const register_map_entry_t ®) { +static void GenerateTargetXMLRegister(std::ostringstream &s, + const uint32_t reg_num, + nub_size_t num_reg_sets, + const DNBRegisterSetInfo *reg_set_info, + const register_map_entry_t ®) { const char *default_lldb_encoding = "uint"; const char *lldb_encoding = default_lldb_encoding; const char *gdb_group = "general"; @@ -5050,7 +5214,7 @@ void GenerateTargetXMLRegister(std::ostringstream &s, const uint32_t reg_num, XMLElementStartEndAttributes(s, true); } -void GenerateTargetXMLRegisters(std::ostringstream &s) { +static void GenerateTargetXMLRegisters(std::ostringstream &s) { nub_size_t num_reg_sets = 0; const DNBRegisterSetInfo *reg_sets = DNBGetRegisterSetInfo(&num_reg_sets); @@ -5089,7 +5253,7 @@ static const char *g_target_xml_footer = "</target>"; static std::string g_target_xml; -void UpdateTargetXML() { +static void UpdateTargetXML() { std::ostringstream s; s << g_target_xml_header << std::endl; @@ -5224,8 +5388,9 @@ rnb_err_t RNBRemote::HandlePacket_jGetDyldProcessState(const char *p) { // a one-level-deep JSON dictionary of key-value pairs. e.g. // jThreadExtendedInfo:{"plo_pthread_tsd_base_address_offset":0,"plo_pthread_tsd_base_offset":224,"plo_pthread_tsd_entry_size":8,"thread":144305}] // -uint64_t get_integer_value_for_key_name_from_json(const char *key, - const char *json_string) { +static uint64_t +get_integer_value_for_key_name_from_json(const char *key, + const char *json_string) { uint64_t retval = INVALID_NUB_ADDRESS; std::string key_with_quotes = "\""; key_with_quotes += key; @@ -5261,9 +5426,9 @@ uint64_t get_integer_value_for_key_name_from_json(const char *key, // Returns true if it was able to find the key name, and sets the 'value' // argument to the value found. -bool get_boolean_value_for_key_name_from_json(const char *key, - const char *json_string, - bool &value) { +static bool get_boolean_value_for_key_name_from_json(const char *key, + const char *json_string, + bool &value) { std::string key_with_quotes = "\""; key_with_quotes += key; key_with_quotes += "\""; @@ -5300,7 +5465,7 @@ bool get_boolean_value_for_key_name_from_json(const char *key, // Returns true if it was able to find the key name, false if it did not. // "ints" will have all integers found in the array appended to it. -bool get_array_of_ints_value_for_key_name_from_json( +static bool get_array_of_ints_value_for_key_name_from_json( const char *key, const char *json_string, std::vector<uint64_t> &ints) { std::string key_with_quotes = "\""; key_with_quotes += key; @@ -6162,6 +6327,21 @@ GetCPUTypesFromHost(nub_process_t pid) { return {cputype, cpusubtype}; } +static bool ProcessRunningWithMemoryTagging(pid_t pid) { +#if __has_include(<os/security_config.h>) + if (__builtin_available(macOS 26.0, iOS 26.0, tvOS 26.0, watchOS 26.0, + visionOS 26.0, driverkit 25.0, *)) { + os_security_config_t config; + int ret = ::os_security_config_get_for_proc(pid, &config); + if (ret != 0) + return false; + + return (config & OS_SECURITY_CONFIG_MTE); + } +#endif + return false; +} + // Note that all numeric values returned by qProcessInfo are hex encoded, // including the pid and the cpu type. @@ -6338,6 +6518,9 @@ rnb_err_t RNBRemote::HandlePacket_qProcessInfo(const char *p) { rep << "vendor:apple;"; + if (ProcessRunningWithMemoryTagging(pid)) + rep << "mte:enabled;"; + #if defined(__LITTLE_ENDIAN__) rep << "endian:little;"; #elif defined(__BIG_ENDIAN__) |
