mbox series

[v6,0/8] Add another way to check tagged addresses on remote targets

Message ID 20240418201039.236867-1-gustavo.romero@linaro.org
Headers show
Series Add another way to check tagged addresses on remote targets | expand

Message

Gustavo Romero April 18, 2024, 8:10 p.m. UTC
This series introduces a new method to check for memory tagged addresses
on remote targets. This new method is based on a new packet, qIsAddressTagged. 

GDB now sends first a qIsAddressTagged packet to the stub for checking an
address and if the stub sends an empty reply GDB attemps to use the current code
path as a fallback mechanism, so no change in stubs not supporting the new
packet is required. Stubs that support the new packet just need to implement
the check and reply accordingly to qIsAddressTagged queries.

This new mechanism allows for checking memory tagged addresses in an OS-agnostic
way, which is necessary when debugging targets that do not support
'/proc/<PID>/smaps', as the current method of reading the smaps contents fails
in such cases.

Updates in v2:
 * Fixed build error "no match for ‘operator!=’ (operand types are ‘packet_result’ and ‘packet_status’)"
   reported by Linaro CI bot, caused by a last-minute rebase
 * Added instructions on how to test the series on a remote target using
   QEMU gdbstub (-g option) -- see below

Updates in v3:
 * Changed packet name to qMemTagCheckAddr for consistence
 * Documented the new packet in gdb.texinfo and NEWS
 * Changed target hook name to is_address_tagged
 * Fixed several GNU Style nonconformities
 * Split commit that adds the target hook and the qMemTagCheckAddr in
   two commits
 * Tested fallback mechanism using gdbserver (use of vFile requests
   instead of qMemTagCheckAddr)
 * Fixed off-by-one error
 * Changed targe hook signature to take gdbarch as an argument for
   better modularity

Updates in v4:
 * Changed packet name to qIsAddressTagged as per Luis's suggestion
 * Removed the need for memory-tagging-check-add+ feature in qSupport to
   use the qIsAddressTagged packet; now GDB first attempts to use the
   packet to check the address and if the stub returns empty the fallback
   mechanism (the current code path that reads smaps) is used
 * Fixed documentation as per Eli's review
 * Added unittests for qIsAddressTagged request and replies
 * Fixed "gdb: Introduce is_address_tagged target hook" commit message
 * Removed wrong assert in aarch64_linux_tagged_address_p that crashed
   GDB, for instance, on "memory-tag check 0x0", because 0x0 address is
   actually valid in this context
 * Added several comments in the code as per Luis's reviews

Updates in v5:
 * Use of GDB feature auto detect to find out if the qIsAddressTagged packet
   is supported by the stub, hence this packet is sent only one time if
   it's not supported by the stub
 * More fixes in the documentation
 * Use of reference instead of pointer for param. 'tagged' in
   check_is_address_tagged_reply function
 * Adjusted unit test cases as per Luis's comments
 * Updated QEMU prototype so the stub now replies to qIsAddressTagged queries,
   for testing this series using QEMU (as per comments about testing below)

Updates in v6:
 * Fix cindex position in gdb.texinfo doc file
 * Handle malformed reply of incorrect length (!= 2 hex digits)
 * Adjust unit tests to test the checking of such a malformed reply

----

This series can be tested with the 'mte_t' binary found in:
https://people.linaro.org/~gustavo.romero/gdb, using the GDB
'memory-tag print-allocation-tag' command to show the allocation
tag for array pointer 'a'. To download mte_t:

$ wget https://people.linaro.org/~gustavo.romero/gdb/mte_t
$ chmod +x ./mte_t

... or build it from source:

$ wget https://people.linaro.org/~gustavo.romero/gdb/mte_t.c
$ gcc -march=armv8.5-a+memtag -static -g3 -O0 mte_t.c -o mte_t

For example, testing the address check for the aarch64_linux_nat.c
target:

gromero@arm64:~/code$ ~/git/binutils-gdb_remote/build/gdb/gdb -q ./mte_t
Reading symbols from ./mte_t...
(gdb) run
Starting program: /home/gromero/code/mte_t 
a[] address is 0xfffff7ffc000
a[0] = 1 a[1] = 2
0x100fffff7ffc000
a[0] = 3 a[1] = 2
Expecting SIGSEGV...

Program received signal SIGSEGV, Segmentation fault
Memory tag violation
Fault address unavailable.
0x0000000000418658 in write ()
(gdb) bt
#0  0x0000000000418658 in write ()
#1  0x000000000040a3bc in _IO_new_file_write ()
#2  0x0000000000409574 in new_do_write ()
#3  0x000000000040ae20 in _IO_new_do_write ()
#4  0x000000000040b55c in _IO_new_file_overflow ()
#5  0x0000000000407414 in puts ()
#6  0x000000000040088c in main () at mte_t.c:119
(gdb) frame 6
#6  0x000000000040088c in main () at mte_t.c:119
119	            printf("...haven't got one\n");
(gdb) memory-tag print-logical-tag a
$1 = 0x1
(gdb) memory-tag print-allocation-tag &a[16]
$2 = 0x0
(gdb)  # Tag mismatch
(gdb) 


Testing address check on a core file:

gromero@arm64:~/code$ ulimit -c unlimited
gromero@arm64:~/code$ ./mte_t
a[] address is 0xffffb3bcc000
a[0] = 1 a[1] = 2
0x900ffffb3bcc000
a[0] = 3 a[1] = 2
Expecting SIGSEGV...
Segmentation fault (core dumped)
gromero@arm64:~/code$ ~/git/binutils-gdb_remote/build/gdb/gdb -q ./mte_t ./core
Reading symbols from ./mte_t...
[New LWP 256036]
Core was generated by `./mte_t'.
Program terminated with signal SIGSEGV, Segmentation fault
Memory tag violation
Fault address unavailable.
#0  0x0000000000418658 in write ()
(gdb) bt
#0  0x0000000000418658 in write ()
#1  0x000000000040a3bc in _IO_new_file_write ()
#2  0x0000000000409574 in new_do_write ()
#3  0x000000000040ae20 in _IO_new_do_write ()
#4  0x000000000040b55c in _IO_new_file_overflow ()
#5  0x0000000000407414 in puts ()
#6  0x000000000040088c in main () at mte_t.c:119
(gdb) frame 6
#6  0x000000000040088c in main () at mte_t.c:119
119	            printf("...haven't got one\n");
(gdb) memory-tag print-logical-tag a
$1 = 0x9
(gdb) memory-tag print-allocation-tag &a[16]
$2 = 0x0
(gdb) # Tag mismatch 
(gdb) 


Finally, testing the new packet on a remote target using QEMU gdbstub
which supports the new 'memory-tagging-check-add+' feature (WIP).

Clone and build QEMU:

$ git clone --depth=1 --single-branch -b mte https://github.com/gromero/qemu.git
$ mkdir qemu/build && cd qemu/build
$ ../configure --target-list=aarch64-linux-user --disable-docs
$ make -j
$ wget https://people.linaro.org/~gustavo.romero/gdb/mte_t
$ chmod +x ./mte_t
$ ./qemu-aarch64 -g 1234 ./mte_t

... and connect to QEMU gdbstub from GDB:

gromero@amd:~/git/binutils-gdb/build$ ./gdb/gdb -q
(gdb) target remote localhost:1234 
Remote debugging using localhost:1234
Reading /tmp/qemu/build/mte_t from remote target...
warning: File transfers from remote targets can be slow. Use "set sysroot" to access files locally instead.
Reading /tmp/qemu/build/mte_t from remote target...
Reading symbols from target:/tmp/qemu/build/mte_t...
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault
Memory tag violation
Fault address unavailable.
0x0000000000407290 in puts ()
(gdb) bt
#0  0x0000000000407290 in puts ()
#1  0x000000000040088c in main () at mte_t.c:119
(gdb) frame 1 
#1  0x000000000040088c in main () at mte_t.c:119
119	
(gdb) memory-tag print-allocation-tag a
$1 = 0x2
(gdb) set debug remote on
(gdb) memory-tag print-allocation-tag a
[remote] Sending packet: $qMemTagAddrCheck:200400000802000#1f
[remote] Received Ack
[remote] Packet received: 01
[remote] Sending packet: $qMemTags:400000802000,1:1#6f
[remote] Received Ack
[remote] Packet received: m02
$2 = 0x2
(gdb) 


Also, below is a test of the fallback mechanism using the gdbserver,
which must use vFile requests instead of the new packet:

In one terminal:

gromero@arm64:~/git/binutils-gdb_remote/build$ ./gdbserver/gdbserver localhost:1234 /home/gromero/code/mte_t

... in another terminal:

gromero@arm64:~/git/binutils-gdb_remote/build$ gdb/gdb -q 
(gdb) target remote localhost:1234 
Remote debugging using localhost:1234
Reading /home/gromero/code/mte_t from remote target...
warning: File transfers from remote targets can be slow. Use "set sysroot" to access files locally instead.
Reading /home/gromero/code/mte_t from remote target...
Reading symbols from target:/home/gromero/code/mte_t...
Reading /home/gromero/.local/lib/debug/.build-id/a1/fb8db7731a11f85efa2ae80005bdb590796021.debug from remote target...
Reading /usr/lib/debug/.build-id/a1/fb8db7731a11f85efa2ae80005bdb590796021.debug from remote target...
0x0000000000400580 in _start ()
(gdb) b 103
Breakpoint 1 at 0x400818: file mte_t.c, line 103.
(gdb) c
Continuing.

Breakpoint 1, main () at mte_t.c:103
103	            set_tag(a);
(gdb) n
105	            printf("%p\n", a);
(gdb) set debug remote on 
(gdb) memory-tag print-allocation-tag a 
[remote] Sending packet: $m400948,4#06
[remote] Packet received: 3f030094
[remote] Sending packet: $m400944,4#02
[remote] Packet received: 60003fd6
[remote] Sending packet: $m400948,4#06
[remote] Packet received: 3f030094
[remote] Sending packet: $vFile:setfs:0#bf
[remote] Packet received: F0
[remote] Sending packet: $vFile:open:2f70726f632f3236353634362f736d617073,0,1c0#b0
[remote] Packet received: F8
[remote] remote_hostio_pread: readahead cache miss 28
[remote] Sending packet: $vFile:pread:8,2001f,0#5f
[remote] Packet received: Feaa;00400000-0047e000 r-xp 00000000 fe:02 5663492                            /home/gromero/code/mte_t\nSize:                504 kB\nKernelPageSize:        4 kB\nMMUPageSize:           4 kB\nRss:                 440 kB\nPss:                 440 kB\nPss_Dirty:            12 kB\nShared_Clean:          0 kB\nShared_Dirty:          0 kB\nPrivate_Clean:       428 kB\nPrivate_Dirty:        12 kB\nReferenced:          440 kB\nAnonymous:            12 kB\nKSM:                   0 kB\nLazyFree:              0 kB\nAnonHugePages:    [3247 bytes omitted]
[remote] remote_hostio_pread: readahead cache miss 29
[remote] Sending packet: $vFile:pread:8,2001f,eaa#56
[remote] Packet received: Fb96;fffff7ffc000-fffff7ffd000 rw-p 00000000 00:00 0 \nSize:                  4 kB\nKernelPageSize:        4 kB\nMMUPageSize:           4 kB\nRss:                   4 kB\nPss:                   4 kB\nPss_Dirty:             4 kB\nShared_Clean:          0 kB\nShared_Dirty:          0 kB\nPrivate_Clean:         0 kB\nPrivate_Dirty:         4 kB\nReferenced:            4 kB\nAnonymous:             4 kB\nKSM:                   0 kB\nLazyFree:              0 kB\nAnonHugePages:         0 kB\nShmemPmdMapped:        0 kB\nFilePmdMap [2459 bytes omitted]
[remote] remote_hostio_pread: readahead cache miss 30
[remote] Sending packet: $vFile:pread:8,2001f,1a40#25
[remote] Packet received: F0;
[remote] Sending packet: $vFile:close:8#b8
[remote] Packet received: F0
[remote] Sending packet: $qMemTags:fffff7ffc000,1:1#15
[remote] Packet received: m0e
$1 = 0xe
(gdb)


Cheers,
Gustavo

Gustavo Romero (8):
  gdb: aarch64: Remove MTE address checking from get_memtag
  gdb: aarch64: Move MTE address check out of set_memtag
  gdb: aarch64: Remove MTE address checking from memtag_matches_p
  gdb: Use passed gdbarch instead of calling current_inferior
  gdb: Introduce is_address_tagged target hook
  gdb: Add qIsAddressTagged packet
  gdb/testsuite: Add unit tests for qIsAddressTagged packet
  gdb: Document qIsAddressTagged packet

 gdb/NEWS                  |  10 +++
 gdb/aarch64-linux-nat.c   |  15 ++++
 gdb/aarch64-linux-tdep.c  |  22 +-----
 gdb/arch-utils.c          |   2 +-
 gdb/arch-utils.h          |   2 +-
 gdb/corelow.c             |  10 +++
 gdb/doc/gdb.texinfo       |  37 +++++++++-
 gdb/gdbarch-gen.h         |   4 +-
 gdb/gdbarch.c             |   2 +-
 gdb/gdbarch_components.py |   2 +-
 gdb/printcmd.c            |  32 ++++----
 gdb/remote.c              | 152 ++++++++++++++++++++++++++++++++++++++
 gdb/target-delegates.c    |  30 ++++++++
 gdb/target.c              |   6 ++
 gdb/target.h              |   6 ++
 15 files changed, 289 insertions(+), 43 deletions(-)