4 ms·
You wouldn't have to preallocate memory. Kernel should know better when it's good time to mark and sweep. Unpinning memory doesn't have to have immediate effect
by mirekrusin 5y ago
You wouldn't have to preallocate memory. Kernel should know better when it's good time to mark and sweep. Unpinning memory doesn't have to have immediate effects. I don't think sidechannel attacks would be possible, the only task for kernel is to deallocate at some unknown point in time dereferenced memory, which by definition should not be visible from the user space?
Maybe a bit far fetched but parts of the kernel (will never happen) or kernel modules (possible) could use it as well?
- dragontamer 5y agoMeltdown is a side-channel over any memory-dereference that is in L1 cache. (by measuring the time it takes to read memory: you can determine what is or isn't in L1 cache). As such, any memory read is vulnerable to a meltdown-like attack unless the appropriate caches are cleared (if the cache is cleared, then the Meltdown gadget takes the same amount of time on all memory and no longer works) Spectre is a side-channel over any branch-predicted loop (and I assume most kernel code has a for-loop __somewhere__ that can serve as a target. Even a stray memcpy is sufficient), where the Spectre gadget can read memory across the timing-attack side channel. (by measuring the time it takes for the branch-prediction to occur in the target for-loop) That's why TLB, L1, and branch-predict data needs to be discarded whenever the kernel code starts executing, to prevent these side-channels from leaking information. These caches / branch predict data is worth many hundreds, maybe thousands, of clock cycles. You don't want to just do kernel task-switches willy-nilly. > Kernel should know better when it's good time to mark and sweep The optimal time to mark and/or sweep depends on the application and goals of the programmer. There's a reason why Java has over 3 garbage collectors for the user to configure. Throughput-based code (ex: number crunching) wants a different garbage collection strategy than UI-based code (aka: what we have here, a grossly latency-optimized GC but probably much worse throughput figures). Throughput-based code is happy with "stop the world", because "stop the world" provably does the fewest garbage collect cycles. "Stop the World" means you __only__ run the GC when you run out of memory, and not a minute sooner!! Because you're out of memory, you have to stop the world, causing large latency spikes randomly. But from a throughput perspective, this is the most efficient: least amount of memory reads/writes or CPU-clock cycles spent doing garbage collect duties.
- mirekrusin 5y agoCouldn't this be solved with strategy hint flags if necessary? I don't understand why you'd have to do task switching in the first place. You can't mark pointers as unused without locking/syncing because kernel can reallocate pointers in the middle of scanning, no? That's the only reason, isn't it? But in kernel you'd know about it and you could mark unused pointers in single, lock/synchronisation free pass, no? In other words you don't need to stop the world if you are in kernel space because you know about all reallocations that are happening?
- dragontamer 5y ago> Couldn't this be solved with strategy hint flags if necessary? How is that easier than just the programmer choosing a garbage-collecting library and/or language that works for their use cases? Ex: Javascript / V8 gets this GC from this blogpost. Java gets their 3 or 4 command-line configurable GCs. Go gets their own. Lispers use their Lisp-gc implementation. Etc. etc. Because each programming community has a particular workflow / paradigm that works for them, each specific garbage collector is explicitly designed for each language's quirks. For example: Lisp "cons" is practically a constant-sized garbage collector, possibly implemented using bitsets because all car/cdrs are of a consistent size. Java is throughput focused by default, because of a large number of server-based customers. And of course: this Javascript / V8 focuses on latency optimization for the best UI responsiveness. > I don't understand why you'd have to do task switching in the first place. Wrong word. I meant system call. Either way, switching "into" kernel-context is expensive if only because of the security procedures necessary. By turning garbage-collection into a system call, you've introduced a potential security issue into the kernel (and therefore: security-related slowdowns). If anything, you want to keep as much functionality outside of the kernel as possible. > You can't mark pointers as unused without locking/syncing because kernel can reallocate pointers in the middle of scanning, no? That's the only reason, isn't it? But in kernel you'd know about it and you could mark unused pointers in single, lock/synchronisation free pass, no? In other words you don't need to stop the world if you are in kernel space because you know about all reallocations that are happening? I would recommend that you write a malloc/free implementation by hand. All garbage collection is, is when free becomes a no-op, and all the work is done in the malloc() call. It turns out that "malloc()" has all the information for which nodes were allocated, as well as which ones are alive and/or dead. The kernel never has this information, because the malloc() call threw it away in the first place. If the programmer _had_ the information, they would have called free(). But the programmer decided that calling free() was either too hard, or error-prone for their use case, and decided to go with the easier malloc-only interface. That means that malloc() now has to work a lot harder to "rediscover" the locations of all pointers that must be freed. It doesn't matter if these routines are in the kernel, in user-space, or taking place on the moon over a TCP/IP port by carrier-pigeons. The fundamental tradeoff you have is that your code must rediscover live vs dead pointers on its own, independently from the programmer.