From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id UEoiFinlkV9pSAAAWB0awg (envelope-from ) for ; Thu, 22 Oct 2020 16:01:45 -0400 Received: by simark.ca (Postfix, from userid 112) id 546CC1EFC7; Thu, 22 Oct 2020 16:01:45 -0400 (EDT) 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 611D31E552 for ; Thu, 22 Oct 2020 16:01:41 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 1CD6E39874CA; Thu, 22 Oct 2020 20:01:41 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1CD6E39874CA DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1603396901; bh=Eo7b4tPBANaVjP4djP+u/UNnhZSYhtsKWiCliNSir7c=; 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=qY8oYSqstHQYrvome4TKyj+sti+VebnWNgWX6iRPXFwaQFxGmqrEPRgEKz+pdnta4 cjsCH4psGOZSMaYwL+Hay6ijqf14ffVR20kVNVHSHEvK05UJWjDsak0b8xcwsiKp1c BFg7KoHZp00IAM5R6JAqPS60IfBeQMz6PIaMvtD8= Received: from mail-qt1-x844.google.com (mail-qt1-x844.google.com [IPv6:2607:f8b0:4864:20::844]) by sourceware.org (Postfix) with ESMTPS id A68173987477 for ; Thu, 22 Oct 2020 20:01:37 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org A68173987477 Received: by mail-qt1-x844.google.com with SMTP id m65so2243341qte.11 for ; Thu, 22 Oct 2020 13:01:37 -0700 (PDT) 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=Eo7b4tPBANaVjP4djP+u/UNnhZSYhtsKWiCliNSir7c=; b=IxDb3R4urlcIteBVhmulKdb2jz2nYsZAh8/DHhmrhixddPa9I8r/QJupIqMYz/uVz9 JFLout6DV5yNqcIxj7LcartnEvd0abfKfd+1rMnc2ZS6TppgP6AS2N3RlclAKxcSpDXG D0DfAb4AfLEE4UD47lj5Bz+ai0n7n8FMslK0rTMNMnIbcuxa76qMYXQcRIH1VdfKzHeG 1VDUmR/VG7DJ4o5dCXrqSeQEQBA8ybIUVOXq6fNpBRCSMeq7eYkcMAWZ+B2lLGrrUMw6 PDcceH2sCe2gqsSEYXfHh/W1xE9CJFw/0EXRkqK29a/stEf3tsDU93El6uWY+vNMJtLi sVpg== X-Gm-Message-State: AOAM531A4J/S1qeqs/p3an8h7UKGDOBviRHj5SEBzBdlViDwIlElsguO lhbNfSm0xvck9MM8AETdOcMwLehRakNYpg== X-Google-Smtp-Source: ABdhPJxP893mLSRe7lOAx0E0RuZT33VOj+4DC402nQ0AA/YMl6dHOBo/ziFLZ7vU3E7xV4/VSsv2sg== X-Received: by 2002:ac8:37ef:: with SMTP id e44mr3638224qtc.342.1603396896641; Thu, 22 Oct 2020 13:01:36 -0700 (PDT) Received: from localhost.localdomain ([2804:7f0:8284:1487:b9b1:f72a:8f1:600]) by smtp.gmail.com with ESMTPSA id a21sm1711208qkk.98.2020.10.22.13.01.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 22 Oct 2020 13:01:36 -0700 (PDT) To: gdb-patches@sourceware.org Subject: [PATCH v2 13/24] Refactor parsing of /proc//smaps Date: Thu, 22 Oct 2020 17:00:03 -0300 Message-Id: <20201022200014.5189-14-luis.machado@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20201022200014.5189-1-luis.machado@linaro.org> References: <20201022200014.5189-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 cce75474d7..e4c3d2fbb1 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