I think we’re saying that it’s been 30+ years since .rodata instead of
.text has been the section for read-only data and that your software will
be *more* portable (particularly looking to the future) if it uses .rodata
instead of .text for data it wants to read-and-not-execute.

I guess you could write a configure test for “doesn’t have working .rodata
so use .text instead” but it was the previous millenium when I last saw a
working system like that…

Philip Guenther

On Saturday, July 27, 2024, Shein Asker <sheinas...@gmail.com> wrote:

> Dear Mr. Claudio, @misc readers,
>
> Thank you for your prompt reply.
>
> I see, OpenBSD has a restriction that .text segments are executable-only
> mapped.
>
> In porting the software, I would like to make work-around modifications,
> preferably without modifying the source.
> Does OpenBSD provide a way to loosen these restrictions?
>
> On Sat, Jul 27, 2024 at 10:22 PM Claudio Jeker <cje...@diehard.n-r-g.com>
> wrote:
>
>> 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