From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id EOhTE1Kf7F/qCgAAWB0awg (envelope-from ) for ; Wed, 30 Dec 2020 10:40:02 -0500 Received: by simark.ca (Postfix, from userid 112) id 493D31F0B8; Wed, 30 Dec 2020 10:40:02 -0500 (EST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-1.1 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,MAILING_LIST_MULTI,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.2 Received: from sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id ED4501F0AA for ; Wed, 30 Dec 2020 10:39:55 -0500 (EST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 4F26A388E809; Wed, 30 Dec 2020 15:39:55 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 4F26A388E809 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1609342795; bh=0OWRvGdf7T/Nz0d06877IJT5i5kVoX4tY7mYbakhCcM=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=Bc3NG5oJCtOiK7yjgGBb9W6a29Tgtg1tdfdROtjueZ4pk40g6wf7229nWmE0ziA6T aefauScJWi1fQGF3UZbjTNa5IZfjUm7pQ4kf0EmCSqdJqIYIwjnYeNUXUvvqjyITfO QhqoqE5g4+eRQrv1ruBNRHnAsmEoz5wxXZTSKI4Q= Received: from mail-qv1-xf29.google.com (mail-qv1-xf29.google.com [IPv6:2607:f8b0:4864:20::f29]) by sourceware.org (Postfix) with ESMTPS id 1602F388E801 for ; Wed, 30 Dec 2020 15:39:52 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 1602F388E801 Received: by mail-qv1-xf29.google.com with SMTP id h16so7826989qvu.8 for ; Wed, 30 Dec 2020 07:39:52 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=0OWRvGdf7T/Nz0d06877IJT5i5kVoX4tY7mYbakhCcM=; b=IF50Auf89bNU4v3SHZEw673SQLLkkyf2u1WQ9iRR+eWoD5U9mXB4X9Wvv0ZKZSH538 xgnlfHgXjUrdIiohQF3QaU3Ao6fL47iFqJCqN178TzktiGIc+jcWbpClE80WfKqpZHHx Z4NJQ7wpeAqhZNgC4xxc3VdKJRYyAiJpyQBKik3XC5Nom4FBUs5Max9eMHwLk3z+A4Ry U7jsWBfcM8rlujwqaHB31xtfG6o19zbN3TqzoqeVuUA8YiQLn0fbY1F9tXY7Gtyhw6H7 8m6I+ijZSGEmzfO+fMJmzXStqVUiR64VO51qeerB3lhISWEPfPCg+/x9YASd2tbYFubG 2j8w== X-Gm-Message-State: AOAM530lCS6TyXQ5kR/5T61awemUTUCicGsCDWjeU7cWmXSem89RG8Ts YHwThXL+AXzFGpCMq6JSMy4ThEa7WvSZ8g== X-Google-Smtp-Source: ABdhPJwy+GueJ03NMcpDgv4JAnlqrgACcEjn7832eQBR8tXNeMJBG1gFD2MumUjYg473SxFba8GU8Q== X-Received: by 2002:ad4:4b72:: with SMTP id m18mr56943761qvx.10.1609342791128; Wed, 30 Dec 2020 07:39:51 -0800 (PST) Received: from localhost.localdomain ([2804:7f0:8284:370e:c9ba:c4ec:737b:57da]) by smtp.gmail.com with ESMTPSA id f6sm28589336qkh.2.2020.12.30.07.39.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Dec 2020 07:39:50 -0800 (PST) To: gdb-patches@sourceware.org Subject: [PATCH v4 14/25] Refactor parsing of /proc//smaps Date: Wed, 30 Dec 2020 12:39:05 -0300 Message-Id: <20201230153916.1586725-15-luis.machado@linaro.org> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201230153916.1586725-1-luis.machado@linaro.org> References: <20201230153916.1586725-1-luis.machado@linaro.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Luis Machado via Gdb-patches Reply-To: Luis Machado Cc: david.spickett@linaro.org Errors-To: gdb-patches-bounces@sourceware.org Sender: "Gdb-patches" Updates on v4: - Update return type of parse_smaps_data. - Misc formatting fixes -- The Linux kernel exposes the information about MTE-protected pages via the proc filesystem, more specifically through the smaps file. What we're looking for is a mapping with the 'mt' flag, which tells us that mapping was created with a PROT_MTE flag and, thus, is capable of using memory tagging. We already parse that file for other purposes (core file generation/filtering), so this patch refactors the code to make the parsing of the smaps file reusable for memory tagging. The function linux_address_in_memtag_page uses the refactored code to allow querying for memory tag support in a particular address, and it gets used in the next patch. gdb/ChangeLog: YYYY-MM-DD Luis Machado * linux-tdep.c (struct smaps_vmflags) : New flag bit. (struct smaps_data): New struct. (decode_vmflags): Handle the 'mt' flag. (parse_smaps_data): New function, refactored from linux_find_memory_regions_full. (linux_address_in_memtag_page): New function. (linux_find_memory_regions_full): Refactor into parse_smaps_data. * linux-tdep.h (linux_address_in_memtag_page): New prototype. --- gdb/linux-tdep.c | 359 +++++++++++++++++++++++++++++++---------------- gdb/linux-tdep.h | 4 + 2 files changed, 241 insertions(+), 122 deletions(-) diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index 0e119869e9..a6a06a9742 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -86,8 +86,33 @@ struct smaps_vmflags /* Is this a MAP_SHARED mapping (VM_SHARED, "sh"). */ unsigned int shared_mapping : 1; + + /* Memory map has memory tagging enabled. */ + + unsigned int memory_tagging : 1; }; +/* Data structure that holds the information contained in the + /proc//smaps file. */ + +struct smaps_data +{ + ULONGEST start_address; + ULONGEST end_address; + std::string filename; + struct smaps_vmflags vmflags; + bool read; + bool write; + bool exec; + bool priv; + bool has_anonymous; + bool mapping_anon_p; + bool mapping_file_p; + + ULONGEST inode; + ULONGEST offset; +}; + /* Whether to take the /proc/PID/coredump_filter into account when generating a corefile. */ @@ -474,6 +499,8 @@ decode_vmflags (char *p, struct smaps_vmflags *v) v->exclude_coredump = 1; else if (strcmp (s, "sh") == 0) v->shared_mapping = 1; + else if (strcmp (s, "mt") == 0) + v->memory_tagging = 1; } } @@ -1269,6 +1296,181 @@ typedef int linux_dump_mapping_p_ftype (filter_flags filterflags, ULONGEST addr, ULONGEST offset); +/* Helper function to parse the contents of /proc//smaps into a data + structure, for easy access. + + DATA is the contents of the smaps file. The parsed contents are stored + into the SMAPS vector. */ + +static std::vector +parse_smaps_data (const char *data, + const std::string maps_filename) +{ + char *line, *t; + + gdb_assert (data != nullptr); + + line = strtok_r ((char *) data, "\n", &t); + + std::vector smaps; + + while (line != NULL) + { + ULONGEST addr, endaddr, offset, inode; + const char *permissions, *device, *filename; + struct smaps_vmflags v; + size_t permissions_len, device_len; + int read, write, exec, priv; + int has_anonymous = 0; + int mapping_anon_p; + int mapping_file_p; + + memset (&v, 0, sizeof (v)); + read_mapping (line, &addr, &endaddr, &permissions, &permissions_len, + &offset, &device, &device_len, &inode, &filename); + mapping_anon_p = mapping_is_anonymous_p (filename); + /* If the mapping is not anonymous, then we can consider it + to be file-backed. These two states (anonymous or + file-backed) seem to be exclusive, but they can actually + coexist. For example, if a file-backed mapping has + "Anonymous:" pages (see more below), then the Linux + kernel will dump this mapping when the user specified + that she only wants anonymous mappings in the corefile + (*even* when she explicitly disabled the dumping of + file-backed mappings). */ + mapping_file_p = !mapping_anon_p; + + /* Decode permissions. */ + read = (memchr (permissions, 'r', permissions_len) != 0); + write = (memchr (permissions, 'w', permissions_len) != 0); + exec = (memchr (permissions, 'x', permissions_len) != 0); + /* 'private' here actually means VM_MAYSHARE, and not + VM_SHARED. In order to know if a mapping is really + private or not, we must check the flag "sh" in the + VmFlags field. This is done by decode_vmflags. However, + if we are using a Linux kernel released before the commit + 834f82e2aa9a8ede94b17b656329f850c1471514 (3.10), we will + not have the VmFlags there. In this case, there is + really no way to know if we are dealing with VM_SHARED, + so we just assume that VM_MAYSHARE is enough. */ + priv = memchr (permissions, 'p', permissions_len) != 0; + + /* Try to detect if region should be dumped by parsing smaps + counters. */ + for (line = strtok_r (NULL, "\n", &t); + line != NULL && line[0] >= 'A' && line[0] <= 'Z'; + line = strtok_r (NULL, "\n", &t)) + { + char keyword[64 + 1]; + + if (sscanf (line, "%64s", keyword) != 1) + { + warning (_("Error parsing {s,}maps file '%s'"), + maps_filename.c_str ()); + break; + } + + if (strcmp (keyword, "Anonymous:") == 0) + { + /* Older Linux kernels did not support the + "Anonymous:" counter. Check it here. */ + has_anonymous = 1; + } + else if (strcmp (keyword, "VmFlags:") == 0) + decode_vmflags (line, &v); + + if (strcmp (keyword, "AnonHugePages:") == 0 + || strcmp (keyword, "Anonymous:") == 0) + { + unsigned long number; + + if (sscanf (line, "%*s%lu", &number) != 1) + { + warning (_("Error parsing {s,}maps file '%s' number"), + maps_filename.c_str ()); + break; + } + if (number > 0) + { + /* Even if we are dealing with a file-backed + mapping, if it contains anonymous pages we + consider it to be *also* an anonymous + mapping, because this is what the Linux + kernel does: + + // Dump segments that have been written to. + if (vma->anon_vma && FILTER(ANON_PRIVATE)) + goto whole; + + Note that if the mapping is already marked as + file-backed (i.e., mapping_file_p is + non-zero), then this is a special case, and + this mapping will be dumped either when the + user wants to dump file-backed *or* anonymous + mappings. */ + mapping_anon_p = 1; + } + } + } + /* Save the smaps entry to the vector. */ + struct smaps_data map; + + map.start_address = addr; + map.end_address = endaddr; + map.filename = filename; + map.vmflags = v; + map.read = read? true : false; + map.write = write? true : false; + map.exec = exec? true : false; + map.priv = priv? true : false; + map.has_anonymous = has_anonymous; + map.mapping_anon_p = mapping_anon_p? true : false; + map.mapping_file_p = mapping_file_p? true : false; + map.offset = offset; + map.inode = inode; + + smaps.emplace_back (map); + } + + return smaps; +} + +/* See linux-tdep.h. */ + +bool +linux_address_in_memtag_page (CORE_ADDR address) +{ + if (current_inferior ()->fake_pid_p) + return false; + + pid_t pid = current_inferior ()->pid; + + std::string smaps_file = string_printf ("/proc/%d/smaps", pid); + + gdb::unique_xmalloc_ptr data + = target_fileio_read_stralloc (NULL, smaps_file.c_str ()); + + if (data == nullptr) + return false; + + std::vector smaps; + + /* Parse the contents of smaps into a vector. */ + smaps = parse_smaps_data (data.get (), smaps_file); + + for (const smaps_data &map : smaps) + { + /* Is the address within [start_address, end_address) in a page + mapped with memory tagging? */ + if (address >= map.start_address + && address < map.end_address + && map.vmflags.memory_tagging) + return true; + } + + return false; +} + /* List memory regions in the inferior for a corefile. */ static int @@ -1319,137 +1521,50 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch, /* Older Linux kernels did not support /proc/PID/smaps. */ maps_filename = string_printf ("/proc/%d/maps", pid); data = target_fileio_read_stralloc (NULL, maps_filename.c_str ()); - } - - if (data != NULL) - { - char *line, *t; - - line = strtok_r (data.get (), "\n", &t); - while (line != NULL) - { - ULONGEST addr, endaddr, offset, inode; - const char *permissions, *device, *filename; - struct smaps_vmflags v; - size_t permissions_len, device_len; - int read, write, exec, priv; - int has_anonymous = 0; - int should_dump_p = 0; - int mapping_anon_p; - int mapping_file_p; - - memset (&v, 0, sizeof (v)); - read_mapping (line, &addr, &endaddr, &permissions, &permissions_len, - &offset, &device, &device_len, &inode, &filename); - mapping_anon_p = mapping_is_anonymous_p (filename); - /* If the mapping is not anonymous, then we can consider it - to be file-backed. These two states (anonymous or - file-backed) seem to be exclusive, but they can actually - coexist. For example, if a file-backed mapping has - "Anonymous:" pages (see more below), then the Linux - kernel will dump this mapping when the user specified - that she only wants anonymous mappings in the corefile - (*even* when she explicitly disabled the dumping of - file-backed mappings). */ - mapping_file_p = !mapping_anon_p; - - /* Decode permissions. */ - read = (memchr (permissions, 'r', permissions_len) != 0); - write = (memchr (permissions, 'w', permissions_len) != 0); - exec = (memchr (permissions, 'x', permissions_len) != 0); - /* 'private' here actually means VM_MAYSHARE, and not - VM_SHARED. In order to know if a mapping is really - private or not, we must check the flag "sh" in the - VmFlags field. This is done by decode_vmflags. However, - if we are using a Linux kernel released before the commit - 834f82e2aa9a8ede94b17b656329f850c1471514 (3.10), we will - not have the VmFlags there. In this case, there is - really no way to know if we are dealing with VM_SHARED, - so we just assume that VM_MAYSHARE is enough. */ - priv = memchr (permissions, 'p', permissions_len) != 0; - - /* Try to detect if region should be dumped by parsing smaps - counters. */ - for (line = strtok_r (NULL, "\n", &t); - line != NULL && line[0] >= 'A' && line[0] <= 'Z'; - line = strtok_r (NULL, "\n", &t)) - { - char keyword[64 + 1]; - if (sscanf (line, "%64s", keyword) != 1) - { - warning (_("Error parsing {s,}maps file '%s'"), - maps_filename.c_str ()); - break; - } + if (data == nullptr) + return 1; + } - if (strcmp (keyword, "Anonymous:") == 0) - { - /* Older Linux kernels did not support the - "Anonymous:" counter. Check it here. */ - has_anonymous = 1; - } - else if (strcmp (keyword, "VmFlags:") == 0) - decode_vmflags (line, &v); + std::vector smaps; - if (strcmp (keyword, "AnonHugePages:") == 0 - || strcmp (keyword, "Anonymous:") == 0) - { - unsigned long number; - - if (sscanf (line, "%*s%lu", &number) != 1) - { - warning (_("Error parsing {s,}maps file '%s' number"), - maps_filename.c_str ()); - break; - } - if (number > 0) - { - /* Even if we are dealing with a file-backed - mapping, if it contains anonymous pages we - consider it to be *also* an anonymous - mapping, because this is what the Linux - kernel does: - - // Dump segments that have been written to. - if (vma->anon_vma && FILTER(ANON_PRIVATE)) - goto whole; - - Note that if the mapping is already marked as - file-backed (i.e., mapping_file_p is - non-zero), then this is a special case, and - this mapping will be dumped either when the - user wants to dump file-backed *or* anonymous - mappings. */ - mapping_anon_p = 1; - } - } - } + /* Parse the contents of smaps into a vector. */ + smaps = parse_smaps_data (data.get (), maps_filename.c_str ()); - if (has_anonymous) - should_dump_p = should_dump_mapping_p (filterflags, &v, priv, - mapping_anon_p, - mapping_file_p, - filename, addr, offset); - else - { - /* Older Linux kernels did not support the "Anonymous:" counter. - If it is missing, we can't be sure - dump all the pages. */ - should_dump_p = 1; - } + for (const struct smaps_data map : smaps) + { + int should_dump_p = 0; - /* Invoke the callback function to create the corefile segment. */ - if (should_dump_p) - func (addr, endaddr - addr, offset, inode, - read, write, exec, 1, /* MODIFIED is true because we - want to dump the mapping. */ - filename, obfd); + if (map.has_anonymous) + { + should_dump_p + = should_dump_mapping_p (filterflags, &map.vmflags, + map.priv, + map.mapping_anon_p, + map.mapping_file_p, + map.filename.c_str (), + map.start_address, + map.offset); + } + else + { + /* Older Linux kernels did not support the "Anonymous:" counter. + If it is missing, we can't be sure - dump all the pages. */ + should_dump_p = 1; } - return 0; + /* Invoke the callback function to create the corefile segment. */ + if (should_dump_p) + { + func (map.start_address, map.end_address - map.start_address, + map.offset, map.inode, map.read, map.write, map.exec, + 1, /* MODIFIED is true because we want to dump + the mapping. */ + map.filename.c_str (), obfd); + } } - return 1; + return 0; } /* A structure for passing information through diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h index 723eec3dc1..6658d21c39 100644 --- a/gdb/linux-tdep.h +++ b/gdb/linux-tdep.h @@ -43,6 +43,10 @@ DEF_ENUM_FLAGS_TYPE (enum linux_siginfo_extra_field_values, struct type *linux_get_siginfo_type_with_fields (struct gdbarch *gdbarch, linux_siginfo_extra_fields); + /* Return true if ADDRESS is within the boundaries of a page mapped with + memory tagging protection. */ +bool linux_address_in_memtag_page (CORE_ADDR address); + typedef char *(*linux_collect_thread_registers_ftype) (const struct regcache *, ptid_t, bfd *, char *, int *, -- 2.25.1