On Monday, 19 December 2022 at 14:06:50 UTC, bauss wrote:
> > What do you think about it? Does this require a new DIP?
Isn't it going to be difficult to properly implement? Since you can't really place data into read-only memory, but you have to protect whole pages ex. VirtualProtect() on Windows. Esepcially with how immutable data can still be allocated through GC. Or am I not understanding something about this at all?
I did mention static immutable and CTFE in my initial message. Some of the immutable data is generated at compile time and can safely go into read-only sections. Right now I'm only interested in trying to improve just this.
But since you mentioned catching write accesses to the immutable data backed by GC allocations, this can be done with some help from extra tools or instrumentation. For example, I did use valgrind to debug the code from https://forum.dlang.org/post/cmtaeuedmdwxjecpcrjh@forum.dlang.org
#include <stddef.h>
#include <valgrind/memcheck.h>
void vg_mark_block(void *p, size_t size)
{
int valgrind_handle = VALGRIND_CREATE_BLOCK(p, size, "MARKED BLOCK");
VALGRIND_MAKE_MEM_NOACCESS(p, size);
}
extern(C) void vg_mark_block(void *p, size_t size) @nogc;
void main() @nogc {
try {
static immutable e = new Exception("test");
vg_mark_block(cast(void*)e, __traits(classInstanceSize, typeof(e)));
throw e;
} catch (Exception e) {
assert(e.msg == "test");
}
}
==3369== Invalid write of size 8
==3369== at 0x4D5BEAE: _d_createTrace (in /usr/lib64/libphobos2.so.0.99.1)
==3369== by 0x4D5D4F9: _d_throwdwarf (in /usr/lib64/libphobos2.so.0.99.1)
==3369== by 0x1091C2: _Dmain (in /tmp/test/test)
==3369== by 0x4D5CEBE: void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int function(char[][])*).runAll().__lambda2() (in /usr/lib64/libphobos2.so.0.99.1)
==3369== by 0x4D5CD6D: void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int function(char[][])*).tryExec(scope void delegate()) (in /usr/lib64/libphobos2.so.0.99.1)
==3369== by 0x4D5CE46: void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int function(char[][])*).runAll() (in /usr/lib64/libphobos2.so.0.99.1)
==3369== by 0x4D5CD6D: void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int function(char[][])*).tryExec(scope void delegate()) (in /usr/lib64/libphobos2.so.0.99.1)
==3369== by 0x4D5CCD6: _d_run_main2 (in /usr/lib64/libphobos2.so.0.99.1)
==3369== by 0x4D5CA9F: _d_run_main (in /usr/lib64/libphobos2.so.0.99.1)
==3369== by 0x10923F: main (in /tmp/test/test)
==3369== Address 0x10c098 is 56 bytes inside a MARKED BLOCK of size 76 client-defined
==3369== at 0x1095A1: vg_mark_block (in /tmp/test/test)
==3369== by 0x1091B3: _Dmain (in /tmp/test/test)
Unfortunately valgrind reports both read and write accesses to this area in the log, so the noise about "invalid reads" needs to be filtered out. It doesn't support marking an address range as read-only out of the box: https://valgrind.org/docs/manual/mc-manual.html#mc-manual.clientreqs (but maybe this can be improved?).
ASAN instrumentation could be also potentially useful in the future for catching write accesses to the immutable data backed by GC allocations. And I'm pleasantly surprised to see that ASAN is already available in LDC. However just like valgrind, right now ASAN doesn't support poisoning a memory area as read-only: https://www.mail-archive.com/address-sanitizer@googlegroups.com/msg01948.html