|
|
Subscribe / Log in / New account

LWN.net Weekly Edition for January 23, 2025

Welcome to the LWN.net Weekly Edition for January 23, 2025

This edition contains the following feature content:

This week's edition also includes these inner pages:

  • Brief items: Brief news items from throughout the community.
  • Announcements: Newsletters, conferences, security updates, patches, and more.

Please enjoy this week's edition, and, as always, thank you for supporting LWN.net.

Comments (none posted)

A look at the recent rsync vulnerability

By Daroc Alden
January 21, 2025

On January 14, Nick Tait announced the discovery of six vulnerabilities in rsync, the popular file-synchronization tool. While software vulnerabilities are not uncommon, the most serious one he announced allows for remote code execution on servers that run rsyncd — and possibly other configurations. The bug itself is fairly simple, but this event provides a nice opportunity to dig into it, show why it is so serious, and consider ways the open-source community can prevent such mistakes in the future.

The vulnerabilities were found by two groups of researchers: Simon Scannell, Pedro Gallegos, and Jasiel Spelman from Google's Cloud Vulnerability Research identified five of them, including the most serious one. Aleksei Gorban, a security researcher at TikTok, discovered the sixth — a race condition in how rsync handles symbolic links.

The background

Rsync has several ways to connect to a remote host. By far the most common configuration is to use SSH (or another remote shell) to start a server process on the remote machine, and then use a socket provided by that remote shell to pass information back and forth. But rsync also supports having a persistent server that accepts TLS or plain TCP connections. This setup is more common for servers that want to permit anonymous access without allowing anonymous shell access, such as distribution mirrors.

Regardless of which underlying transport is in use, rsync speaks a custom protocol of its own design in order to exchange information about which files need to be transferred, and once that is determined, the content of those files. Because the protocol changes over time, and not all users can update at once, rsync needs to be able to use different versions of the protocol in different situations. When the protocol calls for the checksum of a file to be included, the length of that checksum can differ depending on the algorithm used. The longest checksum rsync supports is SHA-512, which has a length of 64 bytes.

The flaw

The flaw turns out to be in validating the length of the checksum received from the client. In io.c, rsync prior to version 3.4.0 (the version that fixes this vulnerability) had this code:

	sum->s2length = protocol_version < 27 ? csum_length : (int)read_int(f);
	if (sum->s2length < 0 || sum->s2length > MAX_DIGEST_LEN) {
		rprintf(FERROR, "Invalid checksum length %d [%s]\n",
			sum->s2length, who_am_i());
		exit_cleanup(RERR_PROTOCOL);
	}

MAX_DIGEST_LEN is 64, to accommodate SHA-512 hashes. On the other hand, sum->s2length is a value sent from the client that is supposed to be the length used for hashes in this particular exchange with the server. Since protocol version 27, it's a dynamic value so that the client can send different sizes of hash to save bandwidth. Rsync also never sends a value larger than 16 — longer hashes always get truncated to that length, and collisions are handled separately. The s2length value is used to control how much data is read into this structure (defined in rsync.h):

    struct sum_buf {
	    OFF_T offset;		/**< offset in file of this chunk */
	    int32 len;			/**< length of chunk of file */
	    uint32 sum1;	    	/**< simple checksum */
	    int32 chain;		/**< next hash-table collision */
	    short flags;		/**< flag bits */
	    char sum2[SUM_LENGTH];	/**< checksum  */
    };

SUM_LENGTH, defined in that same file, is only 16 bytes. So a malicious client could lie about which digest it wanted to use, picking something that produces short hashes, but claiming that it needs the full 64 byte space for each one. This would let it write 48 bytes off the end of a sum_buf structure, potentially overwriting whatever value comes next in memory.

The fix was quietly committed in October by Wayne Davison, but had not yet made it into a rsync release. The fix itself is simple: check s2length against the size of the digest method chosen for this connection, not against MAX_DIGEST_LEN:

    -       if (sum->s2length < 0 || sum->s2length > MAX_DIGEST_LEN) {
    +       if (sum->s2length < 0 || sum->s2length > xfer_sum_len) {

The commit includes some other changes to clean up this code and make it less error-prone, such as taking the sum2 field out of sum_buf and using a dynamically allocated array of hashes for a group of sum_buf structures.

The impact

Experienced C developers know why a heap overflow bug like this is dangerous, but the escalation from a bug that can overwrite part of memory to one that can execute arbitrary code may not be obvious to people who have not encountered it before. While even a single buffer overflow can potentially lead to compromise, in this case the impact of the vulnerability is magnified because a client can send many checksums as part of a single request. So, with enough checksums sent, even though any given sum_buf structure is unlikely to be placed immediately before something useful, there's still a good chance of being able to overwrite something that allows an attacker to exercise more control.

This risk is further magnified by one of the other discovered vulnerabilities, however. By manipulating the checksum length, an attacker can also get the server to compare the last byte of the provided checksum to whatever comes after the sum_buf structure. By repeatedly trying the 256 different possibilities for that byte and seeing which ones cause checksum errors, the client can also read up to 48 bytes beyond a sum_buf structure. This allows an attacker to potentially read and identify what kinds of data is situated after sum_buf structures in memory, and tailor their attack depending on what they find.

Ultimately, the ability to read and write the first 48 bytes of random structures is enough to cause all kinds of havoc in a program. There are no publicly available examples of the vulnerabilities in rsync being exploited, but previous exploits of this type have been used to, for example, overwrite function pointers in structures to cause unexpected code to run, or to overwrite buffer lengths to enable overwriting of entire structures.

The aftermath

In this case, the bug was discovered and fixed before anyone seems to have made use of it. But the incorrect code has been present in rsync for many years; a pessimistic analysis has to suggest that there are more bugs like this lurking in many different fundamental tools such as rsync. In this case, the bug was not even particularly difficult to spot, in principle: the bounds check was simply incorrect. What can the open-source community do to reduce the risk of problems like this?

A standard answer is to exhort developers to switch to memory-safe languages. While this is definitely the most permanent and robust solution, it's also expensive in terms of developer time and effort. There are many millions of lines of C and C++ code that go into a modern Linux distribution, and rewriting all of them is simply infeasible. Another approach is to make these problems harder to exploit, using techniques like bucket allocation, address-space layout randomization, and control-flow integrity. But these techniques all have their limits, because they're really just band-aids, not solutions to the underlying problems presented by memory-unsafe code.

There are a multitude of other answers as well — fuzz testing, sandboxing, and even specialized languages like WUFFS. Ultimately, there is no one solution that is right for every project. But when new, serious vulnerabilities are found in widely-deployed 20-year-old code — it's clear that we need to find techniques that work for the projects that underpin modern Linux distributions and see what we can do to reduce the risk of such vulnerabilities being exploited.

Comments (48 posted)

A mouseless tale: trying for a keyboard-driven desktop

By Joe Brockmeier
January 22, 2025

The computer mouse is a wonderful invention, but for the past few months I've been working to use mine as little as possible for productivity and ergonomic reasons. It should not be surprising that there are quite a few open-source applications, utilities, and configuration options that are either designed to or incidentally assist in creating a keyboard-driven desktop. This includes tiling window management with PaperWM, the Vimium browser extension, Input Remapper, and more.

PaperWM scrollable window tiling

A tiling window manager can make it more practical to avoid using the mouse by letting the window manager arrange application windows automatically, rather than forcing the user to manually move them around and adjust their size. However, the traditional tiling window manager approach can also have downsides if one has a small screen and/or many application windows open. If windows cannot overlap and have to share a 14-inch laptop screen, for example, it does not take long before windows are too small to be usable.

Another problem is that many of the tiling desktop options require a fair amount of configuration and cobbling together an assortment of tools to create a usable desktop environment.

PaperWM solves both problems by providing a scrollable tiling interface for GNOME with a virtual viewport larger than the screen. Windows are opened at the full height of the monitor and each new window is placed to the right of the current window. If the windows take up more real estate than provided by the monitor, the overflow windows are simply placed off the edge of the screen and users can use keyboard shortcuts (or the mouse) to move windows into view as needed. Since PaperWM is a GNOME extension, users don't have to assemble all of the pieces of a desktop environment themselves.

It has a configurable focus mode that dictates where the window that has focus is placed on the screen. The default focus mode allows the focused window to be anywhere on screen. Centered mode, as the name suggests, ensures that the window with focus is in the center of the screen, and edge mode places the focused window at the right edge of the screen. It's not clear to me why anyone would prefer the right‑edge mode, but it's there for users who want it.

Like other tiling window manager implementations, window operations are easily controlled from the keyboard without needing to touch the mouse. Users can move between application windows using the Super key (usually the key with the Windows logo on a PC keyboard) plus the arrow key. That is, to select the window to the left users can just press "Super+left".

[PaperWM]

To switch workspaces, use "Super‑`". To resize windows use "Super‑r". That command toggles the width of the window to one of PaperWM's "useful window widths", which are configurable in the extension's settings. For example, I have the widths set to 25%, 33%, and 50% of the screen's resolution. Windows can, of course, still be expanded to full screen mode with the default GNOME keybinding (F11). The usage section of the PaperWM README has a full rundown of all of the extension's options and how to configure it.

It is also possible, of course, to move and resize windows in PaperWM with the mouse or trackpad if a user prefers to do so. PaperWM supports assigning touchpad gestures for some operations, such as swiping between windows and workspaces.

Finally, PaperWM has what it refers to as a "scratch layer" for windows that float above the tiled layout. This might be useful for a chat client or music player, or for applications like a calculator. The "Ctrl+Super+Esc" shortcut removes a window from the tiling layout and places it in the scratch layer. "Super+Esc" will hide the window or make it visible again, and "Shift+Super+Esc" shows or hides all application windows stored in the scratch layer.

The project is available under the GPLv3 license and was originally developed by Tor Hedin Brønner and Ole Jørgen Brønner. It is currently maintained by Karim Vergnes and Stephen Michel. The most recent version, v47.1.0, was released in October, 2024.

PaperWM is not the only option for scrollable tiling, but it has the virtue of being extremely easy to adopt with a minimum of configuration to get started. Other options include the niri Wayland compositor, Karousel for KDE, and papersway for the Sway and i3 window managers.

Firefox bookmark keywords

Firefox bookmarks can be assigned a keyword that will open the URL directly from the address bar. For example, if a user has bookmarked the current LWN weekly edition, they can edit the bookmark and add a keyword like "week". Then, typing week into Firefox's location bar will automatically open https://lwn.net/current/.

Unfortunately, many users may not even be aware the feature exists because it's essentially hidden. For some reason, Firefox does not present the Keyword field when adding a bookmark, it is only visible when editing a bookmark after it is created.

At least, I only took notice of this feature in 2024 and it's been present in Firefox since at least 2014.

This is particularly useful for pages that are visited frequently or pages that one uses infrequently but are inconvenient to navigate to. For example, it might save several clicks to bookmark the page for filing bugs with one's favorite distribution and assign the bookmark a memorable keyword like "fedbugs" or "debbugs". Then the page is just a "Ctrl-l fedbugs" away. To return focus to the page in Firefox (rather than the location bar) use "Shift-F6".

Vimium Firefox extension

The bookmark keyword trick only goes so far in helping to reduce mouse usage in Firefox. To really reduce rodent reliance, I've turned to the Vimium extension, which brings Vim-like keyboard shortcuts to the browser. It is available for Firefox, Chrome, and Edge browsers but I have only tested it with Firefox. It is an MIT-licensed project that has been in development for more than 15 years, with quite a few contributors over that time.

Once it is installed, hitting "?" will bring up an overlay with Vimium's default keybindings. Like Vim, the Vimium extension is modal; it starts in normal mode, which means that navigation is done with standard alphabetic keys. Vim users will find few surprises here: the "h,j,k,l" keys are used for directional scrolling, "gg" will move to the top of the page, "G" to the bottom, and so forth. To open links using the keyboard, press "f" and Vimium will display little pop-up squares with letters next to each link. Typing the lower-case letters will open the link in the same tab, while typing the upper-case letters will open the link in a new tab.

[Vimium]

Unlike Vim, Vimium does not require entering an insert mode to insert text for forms, etc. Typing "i" does place Vimium in an insert mode, but it would be better named "bypass mode". The insert mode tells Vimium to ignore key commands until the the escape key (Esc) is pressed and it returns to normal mode. This is useful for sites such as Gmail and GitHub, which provide their own shortcuts that may conflict with Vimium's. Users can set up rules to automatically disable some or all of Vimium's keybindings on specific sites to avoid any conflicts.

Finally, Vimium has visual and visual line modes to select text: "v" enters visual mode, and "V" enters visual line mode. Once text is selected, it can be copied (or "yanked") with "y" as with Vim. In normal and visual modes users can copy the current page URL with "yy" and select any link on the page to copy with "yf".

Vimium also features the somewhat questionably named "Vomnibar", which is similar in function to Firefox's location bar. Users can provide a URL or search term, but it will also allow searching through history and bookmarks. The Vomnibar can be summoned with "o" or "O", depending on whether the user would like to open a link in the current tab or a new tab, respectively. Note that Vimium does not disable the browser's location bar, so Ctrl-l still works as expected.

After a few days of using Vimium it was rarely necessary for me to need a mouse to do anything I wanted in Firefox.

Mouseless mail with aerc and Quake Terminal

Some readers may never have played id Software's original Quake, but it was a very popular (and somewhat gory for the time) first-person-shooter game introduced in 1996. It was a rarity in that it was a mainstream game that ran natively on Linux.

It also has the distinction of introducing (or at least popularizing) the concept of a drop-down in-game terminal. Players could press the tilde (~) key to summon the in-game console to run commands to modify the game, and then press it again to hide the console. Being one of the few games available for Linux, it ensured that a generation of early Linux users would carry the idea of a drop-down terminal to the desktop.

Aerc is a relatively recent entry in the terminal-based, keyboard-driven mail-client category. It is extremely configurable, works well with multiple accounts, and (there may be a theme here) uses Vim-like keybindings by default. LWN covered the aerc mail client in October 2024, so I won't spend a lot of time on it here except to say that one can happily use aerc for email without ever needing a mouse and plow through a lot of email in a short time with it.

Since so much of my workflow centers around email, I like to be able to access aerc quickly. Since I am easily distracted, I like to be able to hide aerc just as quickly to avoid being drawn into inbox maintenance when I need to be doing something else. The solution is using a drop-down terminal to summon and dismiss aerc (or any terminal application) with the press of a key.

Some terminal applications include drop-down functionality as part of their feature set, but if not, the Quake Terminal extension for GNOME will provide the feature. After installing the extension, users can choose the terminal that they would like to use with Quake Terminal and set the shortcut to be used when toggling the terminal visibility. The default is "F12".

Even if one does not use a terminal-based email client, the drop-down terminal feature is useful for all sorts of operations where one wants to run a command quickly.

Mouseless Emacs

I have been a Vim user for a very long time, but Emacs is sort of the "house editor" for LWN, so I made a concerted effort to switch when I joined last year. Over time, I have learned to appreciate how much one can do with Emacs, and it has grown on me—after a fair amount of work to bend it to my will.

After a few months of using Emacs's menu and toolbar, and trying to adapt to Emacs's default keybindings for editing text, I realized there was no sense in throwing away more than 25 years developing muscle memory for Vim's keybindings and gave up on that. I embraced evil, the extensible vi layer for Emacs, and Evil vimish-fold to add the text-folding functions from Vim that evil lacks.

But there is much more to Emacs than basic text editing, such as Org mode. To reduce the temptation to use the menu and force myself to learn (or adapt) shortcuts for other operations, I added a configuration to hide the toolbar and menu in my ~/.emacs, and shortcuts to toggle them if absolutely needed:

;; Hide unnecessary toolbars
    (tool-bar-mode -1)
    (menu-bar-mode -1)

;; Shortcuts to show toolbars
    (global-set-key (kbd "C-c Y") 'tool-bar-mode)
    (global-set-key (kbd "C-c M") 'menu-bar-mode)

That not only encouraged using shortcuts, it also reclaimed a bit of screen space that was taken up by the toolbars. Since I didn't need the title bar either, I dispensed with that:

;; Goodbye GNOME title bar
    (setq default-frame-alist '((undecorated . t)))

Remapping keys to mice and more

My goal is to reduce mouse usage as much as possible, without going entirely overboard. For example, there seems to be little point in trying to cobble together a mouseless workflow for using image editors to crop and resize screenshots for publication. It's a job that needs doing only once or twice a week, maximum. A mouseless approach might be possible, but the effort would take a long time to pay off. Likewise, navigating preference dialogs or using GNOME Software to install a Flatpak package is something that is probably possible to do with keyboard only—but the payoff in learning to do so seems minimal.

But, if a user really wants (or needs) to replace the mouse with a keyboard, GNOME's Accessibility features allow users to use the numeric keypad to move the mouse pointer. However, this is slower for me than just using the mouse.

I did want to remap some of my keys to be more useful, though. GNOME is somewhat restrictive in that regard. GNOME Tweaks allows users to remap some keys, but it only provides a set of predefined layout options and does not allow users to create arbitrary remappings. For example, instead of setting Caps Lock to Escape, I wanted to remap it to the Alt+x shortcut that Emacs uses to preface so many commands.

For that, I turned to Input Remapper, an application that can remap keyboard, mouse, joystick, and other controller input. Input Remapper sets up per-device rules to remap keys or mouse inputs. That means, for example, that rules for a laptop keyboard to remap Caps Lock will not carry over to an external USB keyboard. It also means, though, that if one has a gaming keypad or another input device, it might be used to set up really complex remappings.

Input Mapper can also remap input from one device type to another. For example, users can remap keyboard input to mouse input in a more fine-grained way than GNOME's Accessibility features.

Basically, users can remap input by choosing their device, and then recording the input to be replaced and assigning it an event name or macro. For example, to replace the Caps Lock with Alt+x I selected my ancient Microsoft Natural Pro keyboard as the device, and then recorded pressing the Caps Lock key. As output I chose Alt_L + x, then applied the change.

[Input Remapper]

The project lists a number of examples of macros and such that can help in getting started with the application. It is not entirely intuitive, but after setting up a few substitutions it starts to make sense. It might be a project best started on the weekend, since it's easy to get lost down a rabbit hole of customizing input devices.

No doubt there are many other tools and tricks to de-mouse the desktop even further that I have yet to find. Even so, putting all of these tools together has been a fun project that has already increased my productivity and "flow" in a noticeable way.

Comments (43 posted)

The many names of commit 55039832f98c

By Jonathan Corbet
January 16, 2025
The kernel is, on its face, a single large development project, but internally it is better viewed as 100 or so semi-independent projects all crammed into one big tent. Within those projects, there is a fair amount of latitude about how changes are managed, and some subsystems are using that freedom in the search for more efficient ways of working. In the end, though, all of these sub-projects have to work together and interface with kernel-wide efforts, including the stable-release and CVE-assignment processes. For some time, there has been friction between the direct rendering (DRM, or graphics) subsystem and the stable maintainers; that friction recently burst into view in a way that shows some of the limitations of how the kernel community manages patches.

One of the key rules for the stable releases is that every patch must first land in the mainline kernel before being considered for the stable trees. The rule exists to ensure that stable patches have had a bare minimum of wider exposure and testing, but also to ensure that no fixes fall through the cracks and miss the mainline entirely. Exceptions to this rule are rare, and usually involve urgent security fixes. In practice, this rule means that any patch proposed for the stable updates should include a line, in its changelog, providing the mainline commit ID for that patch.

On January 10, this patch to the Xe graphics driver, written by Umesh Nerlige Ramappa, was sent to the mailing list dedicated to potential stable-update patches. It duly included this line in its changelog:

    commit 55039832f98c7e05f1cf9e0d8c12b2490abd0f16 upstream

The only problem is that the commit named there does not, as of this writing, exist in the mainline kernel repository. It does exist in linux-next, and can be expected to land in the mainline during the 6.14 merge window. So this would appear to be a violation of the mainline-first rule — this fix is being proposed for the stable updates before it hits the mainline repository. So something strange is happening here. There is a clue to be found in another line in the patch as posted to the stable mailing list — but not in the version that appears in linux-next:

    (cherry picked from commit 55039832f98c7e05f1cf9e0d8c12b2490abd0f16)

This line can also be found in commit f0ed39830e60, which contains the same fix and is in the mainline; it landed there in time for the 6.13-rc6 prepatch. So the change, as posted to the stable list, is indeed a legitimate stable candidate, but figuring that out takes some extra work, and the upstream citation contained in its changelog must either be corrected, or it will forever refer to a commit that did not, in truth, fix the bug in the mainline.

This situation comes about as a result of the way the DRM subsystem organizes its maintainers. DRM is a large subsystem containing many drivers that are some of the largest components within the kernel. Just like maintaining the kernel in a single repository led to scalability problems many years ago, maintaining DRM that way would not work now. So the DRM subsystem is managed by way of a set of sub-subsystem trees that come together in the drm-next repository. A relatively large number of developers are empowered to commit to these repositories, with the result that the maintainership work is widely distributed.

So far so good; the place where the friction comes in is the DRM community's wide use of cherry-picking to move individual commits between repositories as needed. The commit in question had been given ID 55039832f98c on its way into the drm-next repository; that repository feeds into linux-next, so the commit appears there with that ID. At some point, it was identified as a fix that should go into 6.13 rather than waiting for the 6.14 merge window; that resulted in it being cherry-picked into a separate fixes branch as f0ed39830e60. The "cherry picked from" line was added automatically at that time. Later on, the commit was cherry-picked again into another branch for submission to the stable process, resulting in another line in the changelog:

    (cherry picked from commit f0ed39830e6064d62f9c5393505677a26569bb56)

This is the form in which it found its way to the stable maintainers, one of whom, Greg Kroah-Hartman, was not pleased. This profusion of IDs for a single commit, and the multiple appearances of a commit in the repositories, makes it difficult for the stable maintainers to make decisions about which commits should be backported or have CVE numbers assigned to them.

In response, Kroah-Hartman said:

I give up. You all need to stop it with the duplicated git commit ids all over the place. It's a major pain and hassle all the time and is something that NO OTHER subsystem does.

Yes, I know that DRM is special and unique and running at a zillion times faster with more maintainers than any other subsystem and really, it's bigger than the rest of the kernel combined, but hey, we ALL are a common project here. If each different subsystem decided to have their own crazy workflows like this, we'd be in a world of hurt.

Sometimes, the existence of multiple IDs can cause the fix for a bug to appear to be merged before the bug itself. For example, commit a6dd15981c03, merged for 6.12-rc7, claims to be a fix for commit c9b7c809b89f, which landed in 6.13-rc1. Kernel development moves quickly, but one would be hard put to say that it moves so quickly that, by way of relativistic effects, the causality between a bug and the patch that fixes it can appear to be violated. This sort of apparent inversion between cause and effect is not uncommon in the kernel's repository, and it can definitely lead to confusion.

Most subsystems commit short-term fixes to a separate branch from the outset, then push that branch to Linus Torvalds occasionally. If needed, the maintainers of that subsystem may also merge the fixes branch into their "next release" branch. When this process is followed, the fix will retain the same commit ID throughout, and the kinds of problems described here will not arise.

The DRM maintainers, though, have said clearly that they believe such a process cannot work at the scale seen in that subsystem. Dave Airlie suggested fixing the tools used to maintain the stable releases instead. Simona Vetter explained how things work from a graphic developer's point of view, pointing out that, for most of them, the mainline kernel is a distant downstream consumer of their work. The way a patch appears in drm-next, including its ID, tends to be more relevant; that is why a drm-next ID can be cited in a stable-candidate patch, even if that patch entered the mainline with a different ID.

Vetter said that most of the problems experienced by the stable maintainers can be solved by simply looking at all of the commit IDs found in the changelog, including the cherry-picked ones, when deciding whether a change has appeared in another tree (such as the mainline). Vetter added that: "We won't change our process, because I couldn't find the maintainer volunteers to make that happen", noting also: "This shit is easy, except somehow here we are almost a decade later".

Sasha Levin, another stable-release maintainer, pointed out that problems still remain, especially in cases where a commit ends up in the mainline more than once (which happens reasonably frequently in the DRM subsystem). "So no, this isn't a simple trace-the-cherry-pick-tags exercise". Vetter disagreed, though, providing a detailed description of how the chain of cherry-picking develops and concluding "sometimes you do have to do a bit more cherry-pick tracing than what you've done".

Perhaps what is really on display here is the limitations inherent in using a commit ID to identify a change; that ID is firmly tied to just how a commit lands in a specific branch. Some tools, such as Gerrit, place a separate "change ID" into each changelog to identify a change uniquely through both movements through repositories and revisions by the developer; the kernel community, though, has shown little interest in using change IDs and generally prohibits them from appearing in changelogs.

Toward the end of the conversation, Vetter posted "the generic algorithm" for locating commits in the various trees, acknowledging that: "It's a bit madness, but more importantly, it's scriptable madness". Kroah-Hartman agreed that it is "total madness", saying that it takes far too long to execute, but also seemed resigned to dealing with it. "I know DRM isn't the only offender here, many commits flow through multiple trees at the same time". He said he would work on a solution during the coming merge window. With luck, this work will help to bring an end to this long-running disagreement and reduce the impedance mismatches between different practices across the kernel community.

Comments (34 posted)

Development statistics for 6.13

By Jonathan Corbet
January 20, 2025
The 6.13 development cycle ended on January 19 with the release of the 6.13 kernel. This cycle was, on its surface, one of the slowest we have seen in some time; the LWN merge-window summaries (part 1, part 2) and the KernelNewbies 6.13 page can be consulted for a refresher on all it contains. Here, instead, we will take our usual look at where all of those changes came from.

The 6.13 kernel cycle brought in 12,928 non-merge changesets from 2,001 developers. The changeset count is noteworthy for being the lowest since 5.15 (12,377 changesets) in 2021. If one looks at a plot of changeset traffic for each kernel release (taken from the LWN Kernel Source Database), one sees:

[Changeset counts per
  kernel release]

The immediate impression is that kernel development activity has gone into a decline since the 6.7 release (January 2024) hit a record with 17,284 changesets. Interestingly, though, a plot of the number of developers participating in each release tells a slightly different story:

[Developers per
  kernel release]

The 2,001 developers who contributed to 6.13 are somewhat short of the record (2,090) set with 6.2 in 2023, but the previous release (6.12, at 2,074 developers) came close. Kernel developers may be merging fewer changesets at the moment, but that does not necessarily mean that less work is being done and, in any case, there are as many people working on the kernel as there has ever been.

The most active developers this time around were:

Most active 6.13 developers
By changesets
Thomas Zimmermann 1851.4%
Andy Shevchenko 1581.2%
Sean Christopherson 1471.1%
Jani Nikula 1421.1%
Darrick J. Wong 1130.9%
Christoph Hellwig 1120.9%
Dmitry Baryshkov 1110.9%
Javier Carrasco 1110.9%
Thomas Weißschuh 1020.8%
Ian Rogers 990.8%
Dr. David Alan Gilbert 910.7%
Thomas Gleixner 900.7%
Philipp Hortmann 840.6%
Ville Syrjälä 820.6%
Masahiro Yamada 810.6%
Uwe Kleine-König 800.6%
Dmitry Torokhov 800.6%
Bartosz Golaszewski 790.6%
Mark Brown 790.6%
Kuniyuki Iwashima 780.6%
By changed lines
Philipp Hortmann 7640710.0%
Jan Kara 328304.3%
Dave Penkler 293273.8%
Johannes Berg 261043.4%
Dmitry Baryshkov 118471.6%
Bitterblue Smith 105911.4%
Daniel Machon 98781.3%
Hans Verkuil 94901.2%
Marek Vasut 82821.1%
Detlev Casanova 81211.1%
Andy Shevchenko 77551.0%
Darrick J. Wong 75661.0%
Konrad Dybcio 66610.9%
Taniya Das 58910.8%
Ian Rogers 58310.8%
Dr. David Alan Gilbert 55700.7%
Ivaylo Ivanov 54690.7%
Tomi Valkeinen 52780.7%
Jani Nikula 50380.7%
Zong-Zhe Yang 48320.6%

Thomas Zimmermann topped the by-changesets column with a long list of improvements throughout the graphics subsystem. Andy Shevchenko contributed refactoring and cleanups over much of the driver subsystem. Sean Christopherson continued the ongoing refactoring of the KVM subsystem. Jani Nikula contributed many changes to the Intel i915 graphics driver, and Darrick Wong worked extensively in the XFS filesystem, with much of that work going toward online filesystem-checking functionality.

In the lines-changed column, Philipp Hortmann removed a number of old wireless-network drivers. Jan Kara removed the ReiserFS filesystem. Dave Penkler added the GPIB driver subsystem to the staging tree. Johannes Berg also joined the wireless-driver-removal party, and Dmitry Baryshkov added support for a number of Qualcomm clocks.

If the 6.13 development was a bit quieter than usual, some of the reasons why can be seen in the above list. The extensive refactoring work that has created large changeset counts in previous kernels is mostly absent this time around. The massive amdgpu header-file contributions that have bloated past kernels are also not present in 6.13. Instead, we are seeing the steady pace of development that is always happening, but which can be obscured in the statistics by those larger changes.

Just over half (51.3%) of the commits merged for 6.13 included at least one Reviewed-by tag, but only 8% had Tested-by tags. The top testers and reviewers in 6.13 were:

Test and review credits in 6.13
Tested-by
Daniel Wheeler 14811.9%
Dmitry Osipenko 856.8%
Alex Bennée 554.4%
Neil Armstrong 473.8%
Pucha Himasekhar Reddy 302.4%
Gary Guo 191.5%
Nathan Chancellor 151.2%
Randy Dunlap 151.2%
Nicolin Chen 141.1%
Serge Semin 131.0%
Steev Klimaszewski 131.0%
Philipp Zabel 121.0%
Chris Healy 121.0%
Leo Yan 110.9%
Geert Uytterhoeven 100.8%
Rob Clark 100.8%
Rajneesh Bhardwaj 100.8%
Arnaldo Carvalho de Melo 100.8%
Reviewed-by
Christoph Hellwig 2162.5%
Simon Horman 2112.4%
Krzysztof Kozlowski 1972.3%
Dmitry Baryshkov 1191.4%
Ilpo Järvinen 1121.3%
Eric Dumazet 1111.3%
Rodrigo Vivi 951.1%
Rob Herring 941.1%
Darrick J. Wong 911.1%
Andy Shevchenko 911.1%
AngeloGioacchino Del Regno 861.0%
Geert Uytterhoeven 841.0%
Neil Armstrong 810.9%
Ville Syrjälä 810.9%
David Sterba 780.9%
Jason Gunthorpe 780.9%
Christian König 760.9%
Laurent Pinchart 750.9%

These results do not change much from one release to the next; the people who do this work are in it for the long haul. LWN subscribers may see this KSDB page for more information on the testers, reviewers, and bug reporters for 6.13.

Work on 6.13 was supported by 212 employers (that we know of), a typical number. The top employers were:

Most active 6.13 employers
By changesets
Intel148211.4%
(Unknown)11438.8%
Google9777.5%
Red Hat6985.4%
AMD6445.0%
Linaro6264.8%
(None)5864.5%
SUSE4223.3%
Huawei Technologies3822.9%
Meta3442.6%
Oracle3402.6%
IBM3162.4%
Qualcomm2712.1%
Renesas Electronics2572.0%
NVIDIA2311.8%
NXP Semiconductors2251.7%
Linutronix2121.6%
Arm2101.6%
BayLibre2061.6%
(Consultant)1991.5%
By lines changed
(Unknown)9159912.0%
Intel8224710.8%
Emerson7640710.0%
SUSE423475.5%
Qualcomm390535.1%
Linaro316384.1%
Google304874.0%
AMD284413.7%
Red Hat267273.5%
(None)243703.2%
Oracle152852.0%
Meta151142.0%
Collabora149732.0%
IBM138801.8%
Microchip Technology Inc.136581.8%
Realtek126851.7%
NVIDIA117181.5%
BayLibre106541.4%
Cisco98641.3%
NXP Semiconductors91851.2%

Here, too, there are not many surprises to be found.

There were a number of significant changes in 6.13; the addition of lazy preemption and a lot of important Rust infrastructure may, over time, come to be seen as some of the most important. But the removals this time around are also significant. ReiserFS was once the brightest light among Linux filesystems; it was the first to bring journaling, among other things. It has long since been surpassed and gone out of use, but its role should not be forgotten.

The removal of the old wireless drivers also marks the end of an era of sorts. When Linux first started supporting wireless network interfaces, they were treated much like Ethernet interfaces with a few extra parameters to tweak. The "wireless extensions" interface was added to enable that tweaking. It did not take long to realize that wireless interfaces needed to be treated as a different class of device, and the networking subsystem moved in that direction. But the wireless extensions, as a user-facing interface, had to be supported for many years despite its inability to perform all of the necessary management functions for modern network adapters. Only with 6.13 has it been possible, finally, to remove that support.

ReiserFS was introduced in 2.4.0.4 in 2001, and the wireless extensions came with 2.1.15 in 1996. This kernel release, too, surely includes code that will come to be seen, decades from now, as outmoded and ready for removal. But there will be code to replace it; as of this writing, there are just short of 9,000 changesets in linux-next waiting to be merged for 6.14. That release, too, seems unlikely to set any records for change volume. Even below its peak rate, though, the kernel community is a busy place.

Comments (13 posted)

A revamped Python string-formatting proposal

By Jake Edge
January 22, 2025

The proposal to add a more general facility for string formatting to Python, which we looked at in August 2024, has changed a great deal since, so it merits another look. The changes take multiple forms: a new title for PEP 750 ("Template Strings"), a different mechanism for creating and using templates, a new Template type to hold them, and several additional authors for the PEP. Meanwhile, one controversial part of the original proposal, lazy evaluation of the interpolated values, has been changed so that it requires an explicit opt-in (via lambda); template strings are a generalization of f-strings and lazy evaluation was seen by some as a potentially confusing departure from their behavior.

There are a wide variety of use cases for template strings; the previous title of the PEP referred to creating domain-specific languages using them. Obvious examples are safely handling SQL queries or HTML output with user-supplied input. The PEP also has an example with two different approaches to structured logging using template strings.

Template strings use a character tag before their opening quote that modify the way they are interpreted, much like f-strings do, though there are two main differences. The first is the character tag used to denote them; instead of "f", template strings use a "t". A more fundamental difference is that template strings (also known as "t-strings" for obvious reasons) do not return a string, as f-strings do, but instead return a Template object:

    name = 'world'
    
    fstr = f'Hello, {name}'  # fstr is "Hello, world"
    tstr = t'Hello, {name}'  # tstr is an object of type Template

One of the complaints about the original proposal was that it would have allowed arbitrary function names as tags on a string. Given that people would likely want to use short tags, that would tend to pollute the program namespace with short function names. It would have also precluded adding any other tags to Python down the road; currently, the language has others, such as r"" for raw strings and b"" for byte strings. Had the earlier proposal been adopted, no others could ever be added since some program might be using it for its template strings.

The PEP has been revised several times since we covered it in August; it was updated twice in that original thread in mid-October, on October 17 and then a few days later. That turned an already lengthy thread into something approaching a mega-thread, so the most recent update was posted in its own thread in mid-November. Even there, the PEP has continued to evolve based on suggestions and comments; the version at the time of this writing is covered below, but it could change further before it gets formally proposed to the steering council.

Template

The immutable Template type contains two tuples, one each for the static (strings) and interpolated (interpolations) parts of the string. Entries in the interpolations tuple are Interpolation objects, which store the expression to be evaluated, name in the example above, and its value ('world' from above), along with any format specifications given for the interpolation. Those are the conversion function to be used (e.g. 'r' for repr()) and any output-format specifier (e.g. '.2f' for rounding to two places). The following example, adapted from the PEP, demonstrates how it is meant to work:

    name = "World"
    template = t"Hello {name}"

    assert template.strings[0] == "Hello "
    assert template.interpolations[0].expr == "name"
    assert template.interpolations[0].value == "World"
    
    assert template.strings[1] == ""

The interpolations tuple will have an entry per interpolation site in the template, so it may be empty. The strings tuple will have one entry per interpolation site plus an extra; empty strings will be used for places where there is no static data, such as the end of the string or between two interpolation sites with no spaces between them. For example:

    a = b = 2
    template = t"{a} + {b} = {a+b}"

    assert template.strings[0] == ""
    assert template.interpolations[0].expr == "a"
    assert template.interpolations[0].value == 2

    assert template.strings[1] == " + "
    assert template.interpolations[1].expr == "b"
    assert template.interpolations[1].value == 2

    assert template.strings[2] == " = "
    assert template.interpolations[2].expr == "a+b"
    assert template.interpolations[2].value == 4

    assert template.strings[3] == ""    

The two tuples are meant to be processed in alternating fashion, starting with the first element of strings. An easy way to do that is to iterate over the template; the class provides an __iter__() method so template objects can be used in for loops, for example. It will return strings and interpolations in the order they appear in the template, without any empty strings to enforce the alternation. There is also a values() convenience method that returns a tuple of the value attributes of all interpolations in the template.

The programmer can then provide any sort of template processing that they want by creating a function which operates on a Template. For example, an html() function could provide input sanitizing and allow adding HTML attributes via a dictionary, neither of which is possible using f-strings:

    evil = "<script>alert('evil')</script>"
    template = t"<p>{evil}</p>"
    assert html(template) ==
           "<p>&lt;script&gt;alert('evil')&lt;/script&gt;</p>"

    attributes = {"src": "shrubbery.jpg", "alt": "looks nice"}
    template = t"<img {attributes} />"
    assert html(template) ==
           '<img src="shrubbery.jpg" alt="looks nice" />'

The PEP has a section that shows how f-strings could be implemented using template strings. As with most of the examples, it uses the match-based processing that the PEP authors see as "the expected best practice for many template function implementations". The skeleton of that is as follows:

    for item in template:
        match item:
            case str() as s:
                ... # handle each string part
            case Interpolation() as interpolation:
                ... # handle each interpolation

Originally, the expression for an Interpolation was not evaluated until the template-processing function called a getvalue() method, which was a form of lazy evaluation. In contrast, the interpolations for an f-string are evaluated when it is. Lazy evaluation was removed from the proposal back in October, because of that behavioral difference. Most people, including the PEP authors, think that f-strings should be the starting point for understanding template strings. Template-processing functions can be written to do their own form of lazy evaluation, as the PEP describes; if the function is written to handle a callable as an interpolation, a lambda can be used as the expression. Similarly, asynchronous evaluation can be supported for template strings.

Filters

There has been a lot of discussion over the last six months or so, but there is a sense that most of the objections and suggestions have been handled in one form or another at this point. One that was passed over was Larry Hasting's strong desire for adding a filter mechanism. He noted that several Python template libraries, including Jinja, Django Template Language, and Mako, all have the concept of a filter and, interestingly, all use the pipe ("|") symbol for filters. The basic idea is to be able to process the strings in an interpolation by feeding them to expressions or functions that modify them. A classic use case in the existing libraries is to escape HTML in the interpolated string. He said that it would be a "misstep" not to include filter syntax in the PEP.

Guido van Rossum, who is one of the PEP authors, disagreed with using "|", since it already has established meanings in Python expressions (bitwise or and set union). The interpolations are already Python expressions, however, so filtering "should be part of the expression syntax". He suggested: "If you want a filter operator it should be a proposal for an additional expression operator, not just in the context of t-strings."

Hastings pointed out that the operator is already overloaded, but acknowledged some ambiguity when using it unadorned in an interpolation expression. He had other ideas for ways to use the pipe symbol, but Van Rossum continued to push back:

If we're looking for a filter operator that doesn't conflict with expressions, we could extend !r, !s, and !a with !identifier, keeping the original three as shorthands for !repr, !str and !ascii, respectively.

That would work in f-strings too. But I would recommend making that a separate PEP.

Hastings seemed to like that idea, but had some follow-up questions. Van Rossum wryly pointed out: "Those are all excellent questions for the team working on that PEP — not for me nor for the PEP 750 team. :)" On the other hand, Jinja maintainer David Lord said that he is "not convinced I would add filter syntax if I was rewriting Jinja today"; it has caused confusion with regard to operator precedence and the readability gains are minimal.

Next steps

On January 16, Dave Peck, posted the latest updated version of the PEP. Over the past few months, Peck has been doing the editing on the PEP, as well as being one of the more active participants, among the PEP authors, in the discussions. The next day, another PEP author, Paul Everitt, thought that it was likely that the time had come to "start the process of getting on the steering council's radar, as we've integrated multiple rounds of review and feedback"

PEP 750 is an extensive proposal that has seen a lot of effort, some of it going back well beyond the PEP itself. Beyond just the specification, the PEP contains examples, patterns for processing templates, an extensive collection of rejected ideas, and more. These days, Python has no shortage of string-formatting tools, both in the language itself and in libraries and frameworks of various sorts, but the PEP authors clearly see value in another. Before too long, we will presumably see if the steering council shares their enthusiasm.

Comments (25 posted)

Reviving None-aware operators for Python

By Daroc Alden
January 17, 2025

The idea of adding None-aware operators to Python has sprung up once again. These would make traversing structures with None values in them easier, by short-circuiting lookups when a None is encountered. Almost exactly a year ago, LWN covered the previous attempt to bring the operators to Python, but there have been periodic discussions stretching back to 2015 and possibly before. This time Noah Kim has taken up the cause. After some debate, he eventually settled on redrafting the existing PEP to have a more limited scope, which might finally see it move past the cycle of debate, resurrection, and abandonment that it has been stuck in for most of the last decade.

Mark Haase and Steve Dower first proposed adding None-aware operators to Python in PEP 505 ("None-aware operators"). At the time, they suggested adding three different operators to Python to handle "None coalescing" (using a default option if something is None), None-aware attribute access (accessing an attribute of a potentially None object without raising an exception), and None-aware indexing (the same but for lists and dictionaries rather than objects). In support of these changes, they noted that many other languages have something similar, and that the operators can make code more concise and readable. The exact details of the translation into Python's existing semantics were the topic of some debate, but the simplest version would be something like this:

    # Current code:
    value = a if a is not None else b
    # Alternate current code that is subtly broken for false-y values:
    value = a or b
    # Proposed code:
    value = a ?? b

    # Current code:
    value = a.b if a is not None else None
    # Proposed code:
    value = a?.b

The original discussion of the PEP didn't end up leading to a decision, and neither did any of the follow up discussions in 2018 , 2022, or 2023. In mid-December 2024, Kim posted a topic in the Python discussion forum seeking to revive the PEP, hoping specifically to "solicit feedback on how I can get the process moving again." Emily Morehouse, a member of Python's Steering Council, was in favor of the idea, offering to sponsor the PEP. Guido van Rossum also approved of the PEP, noting that he had found the equivalent operators in TypeScript to be quite useful. He did raise the question of whether a.b?.c should be None when a.b is None, when b is missing from a, or (his preferred option) both.

Lucas Malor agreed that the ?. operator ought to work when an attribute was missing, but thought that the ?? operator should work differently, and raise an exception if its left argument is undefined. Kim disagreed with Van Rossum and partially agreed with Malor, saying that a.b?.c should raise an exception if a exists and is not None, but does not have an attribute b. Over the rest of the discussion, several people expressed support for all of those alternatives and more.

Python-specific concerns

Van Rossum shared his reasoning for the suggestion by comparing the proposed behavior to JavaScript. In that language, the use of undefined to represent missing attributes makes the behavior of the operators more consistent — and therefore useful for traversing JSON maps that have some ad-hoc structure.

Python works different – if you have a JSON dict a that may or may not have a key k, a["k"] raises, and if we want to have a["k"]?.f to return None, it looks like the ? operator would have to catch the KeyError from a["k"]. If we don't catch that exception, ?. would be much less useful than it is in JavaScript. The same reasoning applies to a.f?.g – we'd have to catch the AttributeError.

But he noted that approach was likely to be contentious, given that "there are a lot of people in the Python community (including core devs) who abhor operations that silence exceptions without an except clause". Kim proved to be one such person, saying that while he appreciated Van Rossum's point, he worries that suppressing exceptions "will introduce as many footguns as we've solved." For example, he pointed out that some developers wouldn't want such an operator to suppress all AttributeError exceptions, or it could potentially silently hide bugs in the code. He later said that he would not support the proposal if it suppressed exceptions — something that Brett Cannon wryly pointed out would put Kim in the same group as the original PEP authors, who don't like the idea of any None-aware operators anymore.

Cannon summarized the disagreement as being about safe traversal of structures versus handling None. Ultimately, he thought that no matter which design was picked, it would remain possible to write bad code using it: "You can't protect everyone from every conceivable issue and at some point have to lean on people being consenting adults." Dower thought that Cannon had identified "the key point".

"Sxderp" pointed out that everyone seemed to more or less agree about the meaning of ??, and that the other operators (?. and ?[]) were the ones causing disagreement. They proposed dropping the other operators from the PEP to focus on ??, an approach that Paul Moore agreed with.

At that point, the discussion changed to whether the ?? operator ought to only check for None, or whether there should be some way to make it work with other sentinel values. Cannon was of the opinion that the language should avoid making None "special", since Python is notable for its pervasive ability to let the user override operators and other language features. Van Rossum disagreed, saying that None was "special, intentionally". Many places in the standard library use None as a special case, and new syntax wouldn't change that.

More magic

Several people went back and forth proposing translations into Python's existing syntax before Van Rossum proposed an even more radical idea: introducing a postfix ? operator that generalizes the effects of the other operators to attribute access, indexing, and bare expressions. With this proposal, a single ? operator would handle short-circuiting an entire expression to be None if any part of it was None, or if there were any missing attributes or keys:

    # Using postfix ? on a lookup:
    value = a.b? # Same as original proposal's "a?.b"
    value = a['key1']['key2']?

Kim supported the idea, but it didn't gain many other supporters, since people were already leery about the existing proposal. Even the less-controversial option had a few detractors, though. Hugo van Kemenade quoted Dower's original explanation for why he initially withdrew PEP 505:

The reason I eventually kind of withdrew that is [I had] a really interesting discussion with Raymond Hettinger at one of our core dev sprints where he basically laid out: the features that go into the language actually change the way people program. And it has far more of a follow-on effect than just how that particular thing gets written. It's going to affect how the rest of the code is written.

At the time, Dower became convinced that adding features like None-coalescing would make Python programmers less likely to validate their data, which would cause verification logic to be spread throughout the code. Kim doesn't see that as a problem, but Dower still holds that position, going so far as to say that if Kim doesn't believe that adding these operators will change the way people use Python, then there's not much point in the proposal. So it's up to the proponents of the PEP to demonstrate that the resulting change would be positive.

Van Rossum shared a detailed example of the kinds of problems he was currently dealing with in TypeScript, and what the Python version would look like with and without the new syntax. Moore thought that kind of code was common, but remained unconvinced that this should motivate a change to the language, rather than a simple addition to the standard library to make traversing data with included None values easier. In fact, several people mentioned existing Python libraries designed to help with this use case, especially Pydantic. Supporters of the proposal were not convinced that external libraries were as useful as an extension to the language, however.

A major point of contention with Van Rossum's proposal was the fact that TypeScript effectively relies on its type checker to prevent accesses to undefined attributes. If Python adopted the TypeScript-flavored approach that Van Rossum suggested, a type checker would be all but mandatory in order to catch things that currently cause AttributeError exceptions in Python, once people start using None-aware operators everywhere. Michael H responded to one of Van Rossum's attempted explanations by saying:

Unless you're suggesting that static typing is required for a first-class experience with new features in Python, this isn't actually any different than getattr is, and I think this is actually an argument against the feature.

He went on to point out that, for all of the possible ideas being tossed around in the thread, several people had given examples that were actually incorrect, showing just how difficult a safe-traversal operator would be to understand and use correctly.

The future

By the end of the discussion, the only real conclusion that had been reached was that Kim intended to rewrite a more focused version of the PEP that dealt only with the ?? operator. Whether that less-controversial proposal will get anywhere remains to be seen, but PEP 505 is still provoking a lot of discussion. For example, Guillaume Desforges posted a separate topic pointing out that the discussion was really going around in circles, and asking whether that had happened before and how the community should address it.

The answer is that, while the community has spent a lot of time discussing the PEP, it has never actually been proposed to the Steering Council. They are the people who must ultimately accept or reject it, so until someone does officially propose it, the idea will never be definitively rejected. For the Steering Council to approve a proposal, however, usually requires the community discussion to have reached some kind of agreement.

Ultimately, with so many competing opinions, the Python community is unlikely to settle this particular discussion anytime soon. But if Kim does rewrite PEP 505, it may come closer than it has in a few years to seeing the matter settled.

Comments (36 posted)

Page editor: Jonathan Corbet

Inside this week's LWN.net Weekly Edition

  • Briefs: Kernel 6.13; Dillo 3.2.0; GDB 16.1; OpenVox; Wine 10.0; Quotes; ...
  • Announcements: Newsletters, conferences, security updates, patches, and more.
Next page: Brief items>>

Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds