On Sat, Jul 27, 2024 at 08:14:42PM +0900, Shein Asker wrote: > Dear @misc readers, > > I have recently started using OpenBSD and have encountered the problem > shown in the subject when porting a software used on Linux to OpenBSD. > The problem is outlined as follows: SEGV occurs when trying to read huge > size data placed in a .text section that exceeds the `PAGE_SIZE` by a large > amount. > > My environments are as follows: > machine: > 1. QEMU/KVM x86_64 6.2.0 > 2. Dynabook R63/J (Intel Core i5-7300U) > OS: OpenBSD 7.5 > compiler: clang 16.0.6 > > Below is the minimal code that reproduces the problem. > https://github.com/sheinasker/data-asm/tree/main > > What this code does is to copy the contents of a global string variable > defined in the assembler to a dynamically allocated area and display the > address, size, and leading and trailing data. The entity of `sample_code` > is defined in assembler and its content is a string of 12289 bytes filled > with 'A'. The SEGV occurs in the part of the code below that executes > `memcpy`. > > ```cpp > #include <iostream> > #include <string> > #include <cstring> > > extern "C" char sample_code[]; > extern "C" std::uint32_t sample_code_size; > > int main() { > std::cout << "address: " << reinterpret_cast<void*>(sample_code) << > std::endl; > char* buf = (char*)std::malloc(sample_code_size); > > // SEGV > std::memcpy(buf, sample_code, sample_code_size); > > std::cout << "size: " << std::strlen(buf) << std::endl; > std::cout << "head: " << std::string(buf, buf + 10) << std::endl; > std::cout << "tail: " << std::string(buf + sample_code_size - 11, buf + > sample_code_size - 1) << std::endl; > } > ``` > > Running it with `make run1`, you will see that it crashes with SIGSEGV. > > The log when debugging with `lldb` is as follows: > ``` > openbsd-host$ lldb sample1 > (lldb) target create "sample1" > Current executable set to '/home/asker/src/data-asm/sample1' (x86_64). > (lldb) b main > Breakpoint 1: where = sample1`main, address = 0x0000000000006410 > (lldb) run > Process 8967 launched: '/home/asker/src/data-asm/sample1' (x86_64) > Process 8967 stopped > * thread #1, stop reason = breakpoint 1.1 > frame #0: 0x00000befee364410 sample1`main > sample1`main: > -> 0xbefee364410 <+0>: endbr64 > 0xbefee364414 <+4>: movq 0x372d(%rip), %r11 ; __retguard_831 > 0xbefee36441b <+11>: xorq (%rsp), %r11 > 0xbefee36441f <+15>: pushq %rbp > (lldb) c > Process 8967 resuming > address: 0xbefee361400 > Process 8967 stopped > * thread #1, stop reason = signal SIGSEGV > frame #0: 0x00000bf2b0c282b0 > libc.so.99.0`memcpy(dst0=0x00000bf29066c000, src0=<unavailable>, > length=12289) at memcpy.c:103:2 > (lldb) c > Process 8967 resuming > Process 8967 exited with status = 11 (0x0000000b) > (lldb) q > ``` > > At the same time, the history of system calls was also recorded by > `ktrace`, so that is also shown. > ``` > 8967 sample1 CALL kbind(0x6fe6698ee708,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL kbind(0x6fe6698ee6b8,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL kbind(0x6fe6698ee628,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL kbind(0x6fe6698ee608,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL kbind(0x6fe6698ee628,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL kbind(0x6fe6698ee5d8,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL > mprotect(0xbf24ee36000,0x1000,0x3<PROT_READ|PROT_WRITE>) > 8967 sample1 RET mprotect 0 > 8967 sample1 CALL mprotect(0xbf24ee36000,0x1000,0x1<PROT_READ>) > 8967 sample1 RET mprotect 0 > 8967 sample1 CALL fstat(1,0x6fe6698ee500) > 8967 sample1 STRU struct stat { dev=0, ino=104192, mode=crw--w---- , > nlink=1, uid=1000<"asker">, gid=4<"tty">, rdev=1283, atime=1722062206<"Jul > 27 15:36:46 2024">.276320559, mtime=1722062206<"Jul 27 15:36:46 > 2024">.276320559, ctime=1722062206<"Jul 27 15:36:46 2024">.276320559, > size=0, blocks=0, blksize=65536, flags=0x0, gen=0x0 } > 8967 sample1 RET fstat 0 > 8967 sample1 CALL > mmap(0,0x10000,0x3<PROT_READ|PROT_WRITE>,0x1002<MAP_PRIVATE|MAP_ANON>,-1,0) > 8967 sample1 RET mmap 13137847422976/0xbf2e4ba9000 > 8967 sample1 CALL fcntl(1,F_ISATTY) > 8967 sample1 RET fcntl 1 > 8967 sample1 CALL kbind(0x6fe6698ee6b8,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL kbind(0x6fe6698ee798,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL kbind(0x6fe6698ee738,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL kbind(0x6fe6698ee738,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL kbind(0x6fe6698ee668,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL kbind(0x6fe6698ee568,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL kbind(0x6fe6698ee738,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL kbind(0x6fe6698ee738,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL write(1,0xbf2e4ba9000,0x17) > 8967 sample1 GIO fd 1 wrote 23 bytes > "address: 0xbefee361400 > " > 8967 sample1 RET write 23/0x17 > 8967 sample1 CALL kbind(0x6fe6698ee738,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL kbind(0x6fe6698ee6a8,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL kbind(0x6fe6698ee798,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 CALL > mmap(0,0x4000,0x3<PROT_READ|PROT_WRITE>,0x1002<MAP_PRIVATE|MAP_ANON>,-1,0) > 8967 sample1 RET mmap 13136432644096/0xbf29066c000 > 8967 sample1 CALL kbind(0x6fe6698ee798,24,0x3e7ebd77b6a5befb) > 8967 sample1 RET kbind 0 > 8967 sample1 PSIG SIGSEGV SIG_DFL code=SEGV_ACCERR addr=0xbefee362000 > trapno=6 > 8967 sample1 NAMI "sample1.core" > ``` > > As these logs show, the first address of `sample_code` is 0xbefee361400 and > the first 3072 bytes can be read, but a SEGV occurs when trying to read > 0xbefee362000. Looking at the address values, it appears that this issue is > happening at the boundary of the page. > Oddly enough, if I run the same executable directly after debugging with > lldb, it succeeds, but when I recompile it, the symptoms return. > > > I want to know two things. > - Whether this behavior is due to OpenBSD's protection feature (i.e., > whether it is an OpenBSD specification). > - How to resolve this. > > > As a supplement, when I ran the exact same code on Ubuntu and FreeBSD, no > problems occurred.
On OpenBSD .text segments are mapped execute only. If your hardware support X-only it will crash immediatly, on systems without X-only support the vm fault handler does the check. This is why it crashes on the page boundary. You need to put data into the .data or .rodata section. End of story. -- :wq Claudio