Skip to content

Commit d9398a9

Browse files
committed
[lldb] Remove non-address bits from read/write addresses in lldb
Non-address bits are not part of the virtual address in a pointer. So they must be removed before passing to interfaces like ptrace. Some of them we get way with not removing, like AArch64's top byte. However this is only because of a hardware feature that ignores them. This change updates all the Process/Target Read/Write memory methods to remove non-address bits before using addresses. Doing it in this way keeps lldb-server simple and also fixes the memory caching when differently tagged pointers for the same location are read. Removing the bits is done at the ReadMemory level not DoReadMemory because particualrly for process, many subclasses override DoReadMemory. Tests have been added for read/write at the command and API level, for process and target. This includes variants like Read<sometype>FromMemory. Commands are tested to make sure we remove at the command and API level. "memory find" is not included because: * There is no API for it. * It already has its own address handling tests. Software breakpoints do use these methods but they are not tested here because there are bigger issues to fix with those. This will happen in another change. Reviewed By: omjavaid Differential Revision: https://reviews.llvm.org/D118794
1 parent 3e928c4 commit d9398a9

File tree

6 files changed

+238
-6
lines changed

6 files changed

+238
-6
lines changed

lldb/source/Target/Process.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1919,6 +1919,9 @@ Status Process::DisableSoftwareBreakpoint(BreakpointSite *bp_site) {
19191919
//#define VERIFY_MEMORY_READS
19201920

19211921
size_t Process::ReadMemory(addr_t addr, void *buf, size_t size, Status &error) {
1922+
if (ABISP abi_sp = GetABI())
1923+
addr = abi_sp->FixAnyAddress(addr);
1924+
19221925
error.Clear();
19231926
if (!GetDisableMemoryCache()) {
19241927
#if defined(VERIFY_MEMORY_READS)
@@ -2031,6 +2034,9 @@ size_t Process::ReadMemoryFromInferior(addr_t addr, void *buf, size_t size,
20312034
Status &error) {
20322035
LLDB_SCOPED_TIMER();
20332036

2037+
if (ABISP abi_sp = GetABI())
2038+
addr = abi_sp->FixAnyAddress(addr);
2039+
20342040
if (buf == nullptr || size == 0)
20352041
return 0;
20362042

@@ -2113,6 +2119,9 @@ size_t Process::WriteMemoryPrivate(addr_t addr, const void *buf, size_t size,
21132119

21142120
size_t Process::WriteMemory(addr_t addr, const void *buf, size_t size,
21152121
Status &error) {
2122+
if (ABISP abi_sp = GetABI())
2123+
addr = abi_sp->FixAnyAddress(addr);
2124+
21162125
#if defined(ENABLE_MEMORY_CACHING)
21172126
m_memory_cache.Flush(addr, size);
21182127
#endif

lldb/source/Target/ProcessTrace.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "lldb/Core/Module.h"
1414
#include "lldb/Core/PluginManager.h"
1515
#include "lldb/Core/Section.h"
16+
#include "lldb/Target/ABI.h"
1617
#include "lldb/Target/SectionLoadList.h"
1718
#include "lldb/Target/Target.h"
1819

@@ -80,6 +81,9 @@ Status ProcessTrace::DoDestroy() { return Status(); }
8081

8182
size_t ProcessTrace::ReadMemory(addr_t addr, void *buf, size_t size,
8283
Status &error) {
84+
if (const ABISP &abi = GetABI())
85+
addr = abi->FixAnyAddress(addr);
86+
8387
// Don't allow the caching that lldb_private::Process::ReadMemory does since
8488
// we have it all cached in the trace files.
8589
return DoReadMemory(addr, buf, size, error);

lldb/source/Target/Target.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,6 +1732,12 @@ size_t Target::ReadMemory(const Address &addr, void *dst, size_t dst_len,
17321732
lldb::addr_t *load_addr_ptr) {
17331733
error.Clear();
17341734

1735+
Address fixed_addr = addr;
1736+
if (ProcessIsValid())
1737+
if (const ABISP &abi = m_process_sp->GetABI())
1738+
fixed_addr.SetLoadAddress(abi->FixAnyAddress(addr.GetLoadAddress(this)),
1739+
this);
1740+
17351741
// if we end up reading this from process memory, we will fill this with the
17361742
// actual load address
17371743
if (load_addr_ptr)
@@ -1742,26 +1748,28 @@ size_t Target::ReadMemory(const Address &addr, void *dst, size_t dst_len,
17421748
addr_t load_addr = LLDB_INVALID_ADDRESS;
17431749
addr_t file_addr = LLDB_INVALID_ADDRESS;
17441750
Address resolved_addr;
1745-
if (!addr.IsSectionOffset()) {
1751+
if (!fixed_addr.IsSectionOffset()) {
17461752
SectionLoadList &section_load_list = GetSectionLoadList();
17471753
if (section_load_list.IsEmpty()) {
17481754
// No sections are loaded, so we must assume we are not running yet and
17491755
// anything we are given is a file address.
1750-
file_addr = addr.GetOffset(); // "addr" doesn't have a section, so its
1751-
// offset is the file address
1756+
file_addr =
1757+
fixed_addr.GetOffset(); // "fixed_addr" doesn't have a section, so
1758+
// its offset is the file address
17521759
m_images.ResolveFileAddress(file_addr, resolved_addr);
17531760
} else {
17541761
// We have at least one section loaded. This can be because we have
17551762
// manually loaded some sections with "target modules load ..." or
17561763
// because we have have a live process that has sections loaded through
17571764
// the dynamic loader
1758-
load_addr = addr.GetOffset(); // "addr" doesn't have a section, so its
1759-
// offset is the load address
1765+
load_addr =
1766+
fixed_addr.GetOffset(); // "fixed_addr" doesn't have a section, so
1767+
// its offset is the load address
17601768
section_load_list.ResolveLoadAddress(load_addr, resolved_addr);
17611769
}
17621770
}
17631771
if (!resolved_addr.IsValid())
1764-
resolved_addr = addr;
1772+
resolved_addr = fixed_addr;
17651773

17661774
// If we read from the file cache but can't get as many bytes as requested,
17671775
// we keep the result around in this buffer, in case this result is the
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
C_SOURCES := main.c
2+
CFLAGS_EXTRAS := -march=armv8.5-a+memtag
3+
4+
include Makefile.rules
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
"""
2+
Test that lldb removes non-address bits in situations where they would cause
3+
failures if not removed. Like when reading memory. Tests are done at command
4+
and API level because commands may remove non-address bits for display
5+
reasons which can make it seem like the operation as a whole works but at the
6+
API level it won't if we don't remove them there also.
7+
"""
8+
9+
10+
11+
import lldb
12+
from lldbsuite.test.decorators import *
13+
from lldbsuite.test.lldbtest import *
14+
from lldbsuite.test import lldbutil
15+
16+
17+
class AArch64LinuxNonAddressBitMemoryAccessTestCase(TestBase):
18+
19+
mydir = TestBase.compute_mydir(__file__)
20+
21+
NO_DEBUG_INFO_TESTCASE = True
22+
23+
def setup_test(self):
24+
if not self.isAArch64PAuth():
25+
self.skipTest('Target must support pointer authentication.')
26+
27+
self.build()
28+
self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
29+
30+
lldbutil.run_break_set_by_file_and_line(self, "main.c",
31+
line_number('main.c', '// Set break point at this line.'),
32+
num_expected_locations=1)
33+
34+
self.runCmd("run", RUN_SUCCEEDED)
35+
36+
if self.process().GetState() == lldb.eStateExited:
37+
self.fail("Test program failed to run.")
38+
39+
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
40+
substrs=['stopped',
41+
'stop reason = breakpoint'])
42+
43+
def check_cmd_read_write(self, write_to, read_from, data):
44+
self.runCmd("memory write {} {}".format(write_to, data))
45+
self.expect("memory read {}".format(read_from),
46+
substrs=[data])
47+
48+
@skipUnlessArch("aarch64")
49+
@skipUnlessPlatform(["linux"])
50+
def test_non_address_bit_memory_read_write_cmds(self):
51+
self.setup_test()
52+
53+
# Writes should be visible through either pointer
54+
self.check_cmd_read_write("buf", "buf", "01 02 03 04")
55+
self.check_cmd_read_write("buf_with_non_address", "buf_with_non_address", "02 03 04 05")
56+
self.check_cmd_read_write("buf", "buf_with_non_address", "03 04 05 06")
57+
self.check_cmd_read_write("buf_with_non_address", "buf", "04 05 06 07")
58+
59+
# Printing either should get the same result
60+
self.expect("expression -f hex -- *(uint32_t*)buf", substrs=["0x07060504"])
61+
self.expect("expression -f hex -- *(uint32_t*)buf_with_non_address",
62+
substrs=["0x07060504"])
63+
64+
def get_ptr_values(self):
65+
frame = self.process().GetThreadAtIndex(0).GetFrameAtIndex(0)
66+
buf = frame.FindVariable("buf").GetValueAsUnsigned()
67+
buf_with_non_address = frame.FindVariable("buf_with_non_address").GetValueAsUnsigned()
68+
return buf, buf_with_non_address
69+
70+
def check_api_read_write(self, write_to, read_from, data):
71+
error = lldb.SBError()
72+
written = self.process().WriteMemory(write_to, data, error)
73+
self.assertTrue(error.Success())
74+
self.assertEqual(len(data), written)
75+
buf_content = self.process().ReadMemory(read_from, 4, error)
76+
self.assertTrue(error.Success())
77+
self.assertEqual(data, buf_content)
78+
79+
@skipUnlessArch("aarch64")
80+
@skipUnlessPlatform(["linux"])
81+
def test_non_address_bit_memory_read_write_api_process(self):
82+
self.setup_test()
83+
buf, buf_with_non_address = self.get_ptr_values()
84+
85+
# Writes are visible through either pointer
86+
self.check_api_read_write(buf, buf, bytes([0, 1, 2, 3]))
87+
self.check_api_read_write(buf_with_non_address, buf_with_non_address, bytes([1, 2, 3, 4]))
88+
self.check_api_read_write(buf, buf_with_non_address, bytes([2, 3, 4, 5]))
89+
self.check_api_read_write(buf_with_non_address, buf, bytes([3, 4, 5, 6]))
90+
91+
# Now check all the "Read<type>FromMemory" don't fail
92+
error = lldb.SBError()
93+
# Last 4 bytes are just for the pointer read
94+
data = bytes([0x4C, 0x4C, 0x44, 0x42, 0x00, 0x12, 0x34, 0x56])
95+
written = self.process().WriteMemory(buf, data, error)
96+
self.assertTrue(error.Success())
97+
self.assertEqual(len(data), written)
98+
99+
# C string
100+
c_string = self.process().ReadCStringFromMemory(buf_with_non_address, 5, error)
101+
self.assertTrue(error.Success())
102+
self.assertEqual("LLDB", c_string)
103+
104+
# Unsigned
105+
unsigned_num = self.process().ReadUnsignedFromMemory(buf_with_non_address, 4, error)
106+
self.assertTrue(error.Success())
107+
self.assertEqual(0x42444c4c, unsigned_num)
108+
109+
# Pointer
110+
ptr = self.process().ReadPointerFromMemory(buf_with_non_address, error)
111+
self.assertTrue(error.Success())
112+
self.assertEqual(0x5634120042444c4c, ptr)
113+
114+
@skipUnlessArch("aarch64")
115+
@skipUnlessPlatform(["linux"])
116+
def test_non_address_bit_memory_read_write_api_target(self):
117+
self.setup_test()
118+
buf, buf_with_non_address = self.get_ptr_values()
119+
120+
# Target only has ReadMemory
121+
error = lldb.SBError()
122+
data = bytes([1, 2, 3, 4])
123+
written = self.process().WriteMemory(buf, data, error)
124+
self.assertTrue(error.Success())
125+
self.assertEqual(len(data), written)
126+
127+
addr = lldb.SBAddress()
128+
addr.SetLoadAddress(buf, self.target())
129+
buf_read = self.target().ReadMemory(addr, 4, error)
130+
self.assertTrue(error.Success())
131+
self.assertEqual(data, buf_read)
132+
133+
addr.SetLoadAddress(buf_with_non_address, self.target())
134+
buf_non_address_read = self.target().ReadMemory(addr, 4, error)
135+
self.assertTrue(error.Success())
136+
self.assertEqual(data, buf_non_address_read)
137+
138+
# Read<type>FromMemory are in Target but not SBTarget so no tests for those.
139+
140+
@skipUnlessArch("aarch64")
141+
@skipUnlessPlatform(["linux"])
142+
def test_non_address_bit_memory_caching(self):
143+
# The read/write tests above do exercise the cache but this test
144+
# only cares that the cache sees buf and buf_with_non_address
145+
# as the same location.
146+
self.setup_test()
147+
buf, buf_with_non_address = self.get_ptr_values()
148+
149+
# Enable packet logging so we can see when reads actually
150+
# happen.
151+
log_file = self.getBuildArtifact("lldb-non-address-bit-log.txt")
152+
# This defaults to overwriting the file so we don't need to delete
153+
# any existing files.
154+
self.runCmd("log enable gdb-remote packets -f '%s'" % log_file)
155+
156+
# This should fill the cache by doing a read of buf_with_non_address
157+
# with the non-address bits removed (which is == buf).
158+
self.runCmd("p buf_with_non_address")
159+
# This will read from the cache since the two pointers point to the
160+
# same place.
161+
self.runCmd("p buf")
162+
163+
# Open log ignoring utf-8 decode errors
164+
with open(log_file, 'r', errors='ignore') as f:
165+
read_packet = "send packet: $x{:x}"
166+
read_buf_packet = read_packet.format(buf)
167+
read_buf_with_non_address_packet = read_packet.format(buf_with_non_address)
168+
169+
# We expect to find 1 and only 1 read of buf.
170+
# We expect to find no reads using buf_with_no_address.
171+
found_read_buf = False
172+
for line in f:
173+
if read_buf_packet in line:
174+
if found_read_buf:
175+
self.fail("Expected 1 read of buf but found more than one.")
176+
found_read_buf = True
177+
178+
if read_buf_with_non_address_packet in line:
179+
self.fail("Unexpected read of buf_with_non_address found.")
180+
181+
if not found_read_buf:
182+
self.fail("Did not find any reads of buf.")
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include <linux/mman.h>
2+
#include <sys/mman.h>
3+
#include <unistd.h>
4+
5+
int main(int argc, char const *argv[]) {
6+
size_t page_size = sysconf(_SC_PAGESIZE);
7+
// Note that we allocate memory here because if we used
8+
// stack or globals lldb might read it in the course of
9+
// running to the breakpoint. Before the test can look
10+
// for those reads.
11+
char *buf = mmap(0, page_size, PROT_READ | PROT_WRITE,
12+
MAP_ANONYMOUS | MAP_SHARED, -1, 0);
13+
if (buf == MAP_FAILED)
14+
return 1;
15+
16+
#define sign_ptr(ptr) __asm__ __volatile__("pacdza %0" : "=r"(ptr) : "r"(ptr))
17+
18+
// Set top byte to something.
19+
char *buf_with_non_address = (char *)((size_t)buf | (size_t)0xff << 56);
20+
sign_ptr(buf_with_non_address);
21+
// Address is now:
22+
// <8 bit top byte tag><pointer signature><virtual address>
23+
24+
return 0; // Set break point at this line.
25+
}

0 commit comments

Comments
 (0)