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

Reply via email to