Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Modifying the Linux Kernel – New Syscalls (codingkaiser.blog)
171 points by elirant on Sept 18, 2021 | hide | past | favorite | 42 comments


I remember Synology ships their NAS with custom vendor syscalls that used unassigned syscall numbers at the time, but those numbers are now clashing with real syscalls - causing undefined behaviour when running arbitrary code (e.g. Docker)

So TL;DR only implement custom syscalls if you intend to maintain for all of its consequences in the future.

Here's the tweet on Synology - https://twitter.com/RichFelker/status/1357733309737021444


Synology is a bad example. Many of their custom syscalls are related to filesystem things like btrfs raid5 self repair and carefully removed from their gpl source code. Ianal but imho this is a gpl violation.


Technically, it's not a violation. They're releasing code; just not the code. So long as they (offer to, and then actually) provide the source code on request, licensed under GPLv2, they're allowed to do that.

Though it probably is a violation, because they probably don't do that.


How would modifying code be a violation of the GPL?


If the modified code is distributed in binary form but not in source form.


IIRC that's not a violation. The source form needs to be available upon request, though I could be wrong.


If they remove it from their GPL code release but provide it in the GPL licensed binary then it's violating the GPL


Is it though?

It's misleading, I'll give you that, but let's be pedantic (lawyers are)!

They can probably provide the missing bits in dead tree letter format upon written formal request received at their post address.


It's definitely against the license terms. You're supposed to distribute the code that generated the distributed binaries.


> It's definitely against the license terms. You're supposed to distribute the code that generated the distributed binaries.

It does look like Synology are not abiding by the GPL here, but a minor nitpick: GPL v2 has no such requirement to distribute the source with the binaries. But if you don't, you do have to tell people they can get it and provide it in "machine-readable" format on request.

See https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html section 3b


Lawyers aren't that pedantic. That's why law isn't just some computer program. The point is that human interpretation is important ie: terms like "best efforts", "reasonable doubt", etc.

What you're describing would likely not pass as being a reasonable interpretation of the license.


The source to the derived work that you distribute must be available for the user.


Available, not necessarily proactively published.


There must be a safer way to do this - a kernel module and custom ioctls, or what is it?


Yes, create a kernel module which registers a custom device, then change those syscalls to ioctls on that device.


Custom device inode with custom ioctls. This is how binder works on Android.


Don't you think the TL;DR is never add your own syscalls when implementing your own character device driver is almost always the better option if you want something to run in the kernel?


Foolish but that's still a much smarter mistake to make than the ones we've seen from Western Digital exposing system calls over the network where some blogger can root your NAS by sending an abc123 cookie XHR to its unsecured PHP CGI script interface. If the guys making my NAS are smart enough to make mistakes hacking the kernel then that's reassuring.



To totally complete the tutorial, it would have been awesome to have an example of an userspace software using the new syscalls :)


This is an awesome comprehensive How-To guide.

One suggestion, though: Choose higher syscall numbers. The syscall numbers that this guide uses (334) is already taken in a recent kernel update. Syscalls are added to almost every kernel version, so if you choose, say, syscall #400, in a decade or so you'll have to make a choice between your syscall and the new "official" one.


According to the tweet linked above using device local ioctls are better if you’re not upstreaming.


> using device local ioctls are better

Sure but is that not because the tweet concerns a NAS and the ioctls would relate to an actual device(the storage devices)?

Would 'device local ioctls' work in the blog post scenario where it's just a general purpose process attribute?:

> ..we will assign every process a weight


Yes. A device doesn't even need to support reading and writing. The driver simply registers some name in /dev and the userspace code opens it and does ioctl.

You could also register a name under /proc. There is really no reason to have custom system calls.


Hmm, interesting thanks.

Given the name IO..ctl, is this unintended or unavoidable behaviour?


The name probably originally came from controlling i/o ports and devices at a meta level. But they're just a mechanism for sending a chunk of data to the kernel, and if something in the kernel is watching that path (e.g. /sys/foobar) then it'll be in invoked.

This is apparently how audio works on Linux. The kernel abstracts over sound devices, and provides a sort of "virtual device" at /proc/asound (https://www.kernel.org/doc/html/latest/sound/designs/procfil...)


Wow. I cant believe how simple the building of a custom kernel has become. I fiddled with this 15 years ago and it all seemed very unapproachable at the time.

Great writeup, thats going straight to bookmarks.


Interesting, my thought is opposite, that not much has changed - download, unzip, modify (optional), make menuconfig, make, make install, fix grub config, profit. :)

EDIT: it is true however that someone has shown me the steps, I would have wasted a lot of time without their guidance.


Confirms my theory that I was not very bright in my younger years :)

But I also had almost zero help when I first took on Linux, except manpages.


That matters a lot - having someone to show you the ropes makes the learning process much smoother.


Indeed. It was a different world back then. I remember "searching the BBS" not for topics, but for specific files I had heard would help me out :)


Technology has come so far! What used to take overnight on a very expensive new machine can be done in a matter of minutes on a late model laptop. git bisect run, connected to a script that boots a VM to test the kernel is the holy Grail of... something, not sure what, but looking back at how things used to be, that's a feat of software (and hardware, eg CPU vmx extensions) engineering and progress that blows my mind.


Honestly, a lot of these steps feel like BASH-fu fanciness and Debian-specific package management headaches. :)


typo when it says "And let’s start with the implementation of sys_set_weight" the example code says "asmlinkage long sys_get_weight(int weight){"


I'm a little confused on how this tutorial somehow explains several tasks at completely different levels of difficulty (setting up a VM, installing a compiler via apt-get, modifying/updating the bootloader, building a custom kernel, and finally creating a new syscall) with a relatively constant depth of explanations.

It's difficult for me to judge the usefulness of this guide because I'm familiar with those steps, but I'm wondering if it might have been better to define a more narrow scope for this tutorial with certain prerequisites, and going into detail about the ramifications of adding a new syscall? Especially since I feel the guide barely mentions a few interesting details:

1. Only arch/x86/entry/syscalls/syscall_64.tbl is modified in this tutorial, glossing over the fact that that file only affects x86_64 syscalls (disclaimer: I'm 90% sure on this one, but don't quote me on this). Of course I wouldn't expect the tutorial to explain how to modify all the architecture-specific files, but I'd say this fact is worth noting.

2. The user-space story: The post doesn't mention how to actually use this syscall - even though that's one of the critical parts of a syscall. Essentially, if you call open() in a C program, you're not actually issuing any syscall directly. Rather the C library (e.g. glibc) implements an open() function that internally performs the syscall. This is primarily because many syscalls don't map 1:1 to C library functions, in addition to some differences between platforms and architectures. So if you'd want to use the syscall you'd have to implement a syscall wrapper yourself or use the low-level syscall(2) [1] function that allows you to access system calls not provided by glibc.

There is also some political drama attached to this, essentially the glibc maintainers don't want to implement/provide functions for certain Linux syscalls because of certain reasons (IIRC it's mostly about portability concerns, but I think there are some syscalls that the glibc maintainers just don't like for whatever reason).

3. The "What if you really wanted to add a syscall to Linux?" story, which boils down to: The Linux maintainers don't want to add any new syscalls except in very few cases (because syscalls generally can't be changed once they're introduced). Instead, you're supposed to use more flexible kernel interfaces, such as sysfs or debugfs (or previously, ioctl and procfs, but they're frowned upon nowadays and only used in certain areas). See the Linux documentation on adding syscalls [2].

[1] https://man7.org/linux/man-pages/man2/syscall.2.html

[2] https://www.kernel.org/doc/html/v5.14/process/adding-syscall...


This tutorial was the perfect level of depth for me — someone who has experience programming but never experienced modifying the Linux kernel. I don’t need a lot of info on setting up a VM or installing tools. I just need info on where the hooks are in the kernel itself. How do you compile it and where do you put the new stuff in?


It is missing examples of how to call it. Also, that feature would be better implemented by an interface on /proc/$pid


I don't know a thing about how this works, but if it's a collision problem it sounds like these entry points need the equivalent of a GUID.


There aren't very many syscalls in Linux - 335 for x86_64[1], 190 for i386[2], etc. I tend to like GUIDs, but AFAIK it would cause a massive backward-compatibility problem to switch to something other than an integer.

If I'm reading the kernel source[3] correctly, the syscall interface itself accepts a 32-bit unsigned integer for the call number, but only the lowest 16 bits can be used because the upper 16 bits are used to represent the version. That would still mean that something like 80% of the effective ID space is unused at present.

[1] http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x... [2] http://asm.sourceforge.net/syscall.html [3] https://github.com/torvalds/linux/blob/master/ipc/syscall.c


'it would cause a massive backward-compatibility problem to switch to something other than an integer.'

lol. of course.

Suggestion numero two. A dedicated syscall for additional syscalls that has a dedicated parameter for a GUID. We can add a third level in 2050 or so.


Registering paths in /dev, /proc, or /sys seems like the way to go. Slightly slower than a syscall, because the kernel has to do text comparison with the path (such as /dev/foobar) you're reading/writing/ioctling.


In Linux, the system call number is used as an index into an array of function pointers. Making it a GUID won't really help with table lookup performance -- O(1) random access lookup versus O(log n) binary search.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: