From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id CFXLJ9F2qV/vbwAAWB0awg (envelope-from ) for ; Mon, 09 Nov 2020 12:05:21 -0500 Received: by simark.ca (Postfix, from userid 112) id 9F39A1F08D; Mon, 9 Nov 2020 12:05:21 -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 433C51E552 for ; Mon, 9 Nov 2020 12:05:16 -0500 (EST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id EE8443896C27; Mon, 9 Nov 2020 17:05:15 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org EE8443896C27 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1604941516; bh=0EGkuTeQ/jEDzXrVGpK1rCaOBUjHQFFOPamTspnqOwY=; 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=l3UMoQ7FH8r9C8iZqWBwUYDA2VZuzaqK4IonH2QEHiZDDB6TZCsfCVYf69Eb4b4It Ebj2OxbDrawa34oqx3579SabiZPIWla6wvna5FzyGkmGJD5g2MBBSNMako9PEi9kYv HtTVzsOPQbtZBNzIVVV1amJSeycO9s2JKlN2jgqU= Received: from mail-qt1-x842.google.com (mail-qt1-x842.google.com [IPv6:2607:f8b0:4864:20::842]) by sourceware.org (Postfix) with ESMTPS id 8F8313896C33 for ; Mon, 9 Nov 2020 17:05:12 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 8F8313896C33 Received: by mail-qt1-x842.google.com with SMTP id m65so6452986qte.11 for ; Mon, 09 Nov 2020 09:05:12 -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; bh=0EGkuTeQ/jEDzXrVGpK1rCaOBUjHQFFOPamTspnqOwY=; b=k8acQkU2cLqbA7DyQvfbgD9TccsKML50brd4M2vvGKnrOjwAGlXj8YoigTuta8DM4W 493FJnsF58tDT9i52YHgBDm8+Ru+EnU9x1TpH2G+vd31yEXii78ayGmsyLAG75EmKa71 Iq/CJ/Wmm5PID7PbusFxUqSmL42sAulFgHV1amELpl9b3Qz53iT3ZIKbk6zPEALPUAsn sPvEhuYIzLtVj+QtCNcMF9JcjxDnZTUQb6n38RKo8RecUMfvBYIWmjs4jyRa/8PLHW7C nYQT+XbmhPuTFu+cmmQGbaznhSmzAqdw6gmMHLtTcB5yeRS66OI9kpmlnYpyuYOlbkyz IQTQ== X-Gm-Message-State: AOAM532765mdleqQrEbslK2Uap69Bqu9H2dCYvB6h5EIhxc1ruulfRqh 6SgTcaaV6l9v2i5UwmPXxTsmX3HoqaWUFA== X-Google-Smtp-Source: ABdhPJzbA+5GVOn1klCuNoq9/wEmQovOE220fMbf6rbQvVrFsVXAm/RIts64oWy86r7GOhKeRgfeNQ== X-Received: by 2002:ac8:7b30:: with SMTP id l16mr14350956qtu.360.1604941511425; Mon, 09 Nov 2020 09:05:11 -0800 (PST) Received: from localhost.localdomain ([2804:7f0:8284:1487:5c3b:b268:ad95:5f37]) by smtp.gmail.com with ESMTPSA id s2sm5948358qtw.44.2020.11.09.09.05.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 09 Nov 2020 09:05:11 -0800 (PST) To: gdb-patches@sourceware.org Subject: [PATCH v3 13/24] Refactor parsing of /proc//smaps Date: Mon, 9 Nov 2020 14:04:24 -0300 Message-Id: <20201109170435.15766-14-luis.machado@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20201109170435.15766-1-luis.machado@linaro.org> References: <20201109170435.15766-1-luis.machado@linaro.org> 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" 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 | 358 +++++++++++++++++++++++++++++++---------------- gdb/linux-tdep.h | 4 + 2 files changed, 241 insertions(+), 121 deletions(-) diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index bacb61398f..91bdcc133b 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. */ @@ -472,6 +497,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; } } @@ -1267,6 +1294,184 @@ typedef int linux_dump_mapping_p_ftype (filter_flags filterflags, const char *filename, 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 int +parse_smaps_data (const char *data, + std::vector &smaps, + const char *mapsfilename) +{ + char *line, *t; + + gdb_assert (data != nullptr); + + smaps.clear (); + + line = strtok_r ((char *) data, "\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 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'"), mapsfilename); + 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"), + mapsfilename); + 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; + std::string fname (filename); + + map.start_address = addr; + map.end_address = endaddr; + map.filename = fname; + 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 0; +} + +/* 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. */ + parse_smaps_data (data.get (), smaps, smaps_file.c_str ()); + + if (!smaps.empty ()) + { + for (struct 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. */ @@ -1276,8 +1481,7 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch, linux_find_memory_region_ftype *func, void *obfd) { - char mapsfilename[100]; - char coredumpfilter_name[100]; + std::string coredumpfilter_name; pid_t pid; /* Default dump behavior of coredump_filter (0x33), according to Documentation/filesystems/proc.txt from the Linux kernel @@ -1295,10 +1499,9 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch, if (use_coredump_filter) { - xsnprintf (coredumpfilter_name, sizeof (coredumpfilter_name), - "/proc/%d/coredump_filter", pid); + coredumpfilter_name = string_printf ("/proc/%d/coredump_filter", pid); gdb::unique_xmalloc_ptr coredumpfilterdata - = target_fileio_read_stralloc (NULL, coredumpfilter_name); + = target_fileio_read_stralloc (NULL, coredumpfilter_name.c_str ()); if (coredumpfilterdata != NULL) { unsigned int flags; @@ -1308,125 +1511,39 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch, } } - xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/smaps", pid); + std::string mapsfilename = string_printf ("/proc/%d/smaps", pid); gdb::unique_xmalloc_ptr data - = target_fileio_read_stralloc (NULL, mapsfilename); + = target_fileio_read_stralloc (NULL, mapsfilename.c_str ()); if (data == NULL) { /* Older Linux kernels did not support /proc/PID/smaps. */ - xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/maps", pid); - data = target_fileio_read_stralloc (NULL, mapsfilename); + mapsfilename = string_printf ("/proc/%d/maps", pid); + data = target_fileio_read_stralloc (NULL, mapsfilename.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 (data == nullptr) + return 1; - if (sscanf (line, "%64s", keyword) != 1) - { - warning (_("Error parsing {s,}maps file '%s'"), mapsfilename); - break; - } + std::vector smaps; - 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); + /* Parse the contents of smaps into a vector. */ + parse_smaps_data (data.get (), smaps, mapsfilename.c_str ()); - 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"), - mapsfilename); - 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; - } - } - } + if (!smaps.empty ()) + { + for (struct smaps_data map : smaps) + { + int should_dump_p = 0; - if (has_anonymous) - should_dump_p = should_dump_mapping_p (filterflags, &v, priv, - mapping_anon_p, - mapping_file_p, - filename, addr, offset); + 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. @@ -1436,16 +1553,15 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch, /* 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); + 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 0; } - return 1; + return 0; } /* A structure for passing information through diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h index 91c28738f5..32c9c03835 100644 --- a/gdb/linux-tdep.h +++ b/gdb/linux-tdep.h @@ -41,6 +41,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.17.1