<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>alangrow.com</title>
  <subtitle>Browse aymeric&#39;s, an independent, searchable archive of readable snapshots from followed feeds, preserved in Git with aggr.</subtitle>
  <id>https://aggr.aymericbeaumet.com/sources/alangrow.com/</id>
  <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/sources/alangrow.com/"/>
  <link rel="self" type="application/atom+xml" href="https://aggr.aymericbeaumet.com/sources/alangrow.com/atom.xml"/>
  <updated>2026-11-02T21:23:24+00:00</updated>
  <author>
    <name>aymeric&#39;s</name>
  </author>
  <generator version="2.0.0" uri="https://github.com/aymericbeaumet/aggr">aggr</generator>
  <entry>
    <id>urn:aggr:item:1d303cede388a7e4b6e1e77d879f936e885fed43</id>
    <title type="text">A Decade of Kill Bugs</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-11-02-a-decade-of-kill-bugs/"/>
    <link rel="via" type="text/html" href="https://alangrow.com/blog/a-decade-of-kill-bugs"/>
    <published>2026-11-02T21:23:24+00:00</published>
    <updated>2026-11-02T21:23:24+00:00</updated>
    <author>
      <name>alangrow.com by acg</name>
    </author>
    <category term="news"/>
    <category term="historical"/>
    <category term="linux"/>
    <category term="unix"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;hr /&gt;
&lt;p&gt;On the morning of September 24th 2026, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://endcrawl.com&quot;&gt;Endcrawl&lt;/a&gt; had a brief outage. I&#39;d just typed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;sudo kill -TERM -1490947
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And instantly all services went down.&lt;/p&gt;
&lt;p&gt;Right away I blamed myself. Maybe I mistyped that number! Maybe my code had a signal handling bug. Maybe I don&#39;t understand Unix as well as I think I do. Maybe I&#39;m an idiot!&lt;/p&gt;
&lt;p&gt;As it turned out, the outage was caused by a bug in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://man7.org/linux/man-pages/man1/kill.1.html&quot;&gt;&lt;code&gt;kill(1)&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is really surprising. The &lt;code&gt;kill&lt;/code&gt; program is 50 years old. Its &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://pubs.opengroup.org/onlinepubs/9699919799/utilities/kill.html&quot;&gt;spec&lt;/a&gt; is simple and stable. It&#39;s probably exercised a billion times a day.&lt;/p&gt;
&lt;p&gt;A bug in &lt;code&gt;kill&lt;/code&gt; was so surprising that, once I&#39;d found it, I couldn&#39;t stop there. I needed to know how this bug came to be, and what had led the programmers so badly astray. When I eventually found the root cause, there was an even bigger surprise. It had led to not just one bug, but a whole series of &lt;code&gt;kill&lt;/code&gt; bugs that shipped in Ubuntu 14, 16, 18, 22 and 24 LTS.&lt;/p&gt;
&lt;p&gt;This post documents a decade of &lt;code&gt;kill&lt;/code&gt; bugs, then makes some recommendations.&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alangrow.com/blog/a-decade-of-kill-bugs#an-outage-not-understood-happens-again&quot;&gt;An Outage Not Understood Happens Again&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Back to the outage. I scrambled to put the fire out. After that came a surprise second fire. By the end of all this I was pretty demoralized. I was ready to accept that I&#39;d made a dumb mistake and ready to get on with the work day. There was this nagging feeling though…&lt;/p&gt;
&lt;p&gt;Remember that &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=kCXTq-fWWio&quot;&gt;final scene&lt;/a&gt; in Burn After Reading?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;What did we learn Palmer?&amp;quot;&lt;/p&gt;
&lt;p&gt;&amp;quot;I don&#39;t know sir.&amp;quot;&lt;/p&gt;
&lt;p&gt;&amp;quot;I don&#39;t f**king know either. I guess we learned not to do it again.&amp;quot;&lt;/p&gt;
&lt;p&gt;&amp;quot;Yes sir.&amp;quot;&lt;/p&gt;
&lt;p&gt;&amp;quot;Although I&#39;m f**ked if I know what we did...&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What had just happened? Was I about to be hit with a third outage? How could I be sure?&lt;/p&gt;
&lt;p&gt;An outage not understood is an outage that happens again. That nagging feeling I had was a call to understand. With a sigh, I put all my other work on hold and started a proper post mortem.&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alangrow.com/blog/a-decade-of-kill-bugs#the-proximate-cause&quot;&gt;The Proximate Cause&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&#39;s examine the command that caused the outage:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;sudo kill -TERM -1490947
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The negative number here is a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Process_group&quot;&gt;process group identifier&lt;/a&gt;, or pgid for short. A pgid is like a pid, but identifies a group of related processes. By convention, killing a positive number sends a signal to a single process, while killing a negative number signals all processes in that process group.&lt;/p&gt;
&lt;p&gt;I was attempting to cancel a big Endcrawl 7k test render. A normal person would have killed one of the processes involved and let failures cascade to the others. But killing the whole group simultaneously strikes me as cleaner.&lt;/p&gt;
&lt;p&gt;Process group signaling isn&#39;t some exotic operation. You use it all the time. Every time you &lt;code&gt;^C&lt;/code&gt; a command, the tty sends &lt;code&gt;SIGINT&lt;/code&gt; to the foreground process group. Interactive shells run all but the simplest commands in a new process group, which among other things makes &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Job_control_(Unix)&quot;&gt;job control&lt;/a&gt; possible.&lt;/p&gt;
&lt;p&gt;Process group signaling is in fact an awesome Unix innovation. I love it so much I &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/bruceg/daemontools-encore/pull/16&quot;&gt;added it to daemontools&lt;/a&gt;, where &lt;code&gt;svc -+h&lt;/code&gt; now avoids orphans in a multiprocess service bounce.&lt;/p&gt;
&lt;p&gt;At any rate, imagine my surprise when the command didn&#39;t just kill those render processes, but killed &lt;em&gt;all processes on the machine&lt;/em&gt; save one!&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alangrow.com/blog/a-decade-of-kill-bugs#the-bug&quot;&gt;The Bug&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here&#39;s the relevant snippet from &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gitlab.com/procps-ng/procps/-/releases/v4.0.4&quot;&gt;procps 4.0.4&lt;/a&gt;, the provider of &lt;code&gt;/bin/kill&lt;/code&gt; in Ubuntu 24.04 LTS. Can you spot the bug?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;CSS&quot;&gt;&lt;span class=&quot;syntax-source syntax-css&quot;&gt;&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;opterr=0; &lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-block syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;/*&lt;/span&gt; suppress errors on -123 &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;while ((&lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-css&quot;&gt;i&lt;/span&gt; = getopt_long(argc, argv, &amp;quot;l&lt;span class=&quot;syntax-entity syntax-other syntax-pseudo-element syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-entity syntax-css&quot;&gt;::&lt;/span&gt;Ls&lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-other syntax-pseudo-class syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-entity syntax-css&quot;&gt;:&lt;/span&gt;hVq&lt;/span&gt;:&amp;quot;, longopts, &amp;amp;optindex&lt;/span&gt;)) != -1)
&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;  &lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-css&quot;&gt;switch&lt;/span&gt; (&lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-css&quot;&gt;i&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;syntax-meta syntax-property-list syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-comment syntax-block syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;/*&lt;/span&gt; ... &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;case&lt;/span&gt; &amp;#39;?&amp;#39;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-key-value syntax-css&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-property-value syntax-css&quot;&gt;
      if (!isdigit(optopt&lt;/span&gt;)) {
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;xwarnx&lt;/span&gt;(_(&amp;quot;&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;invalid&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;argument&lt;/span&gt; %&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;c&lt;/span&gt;&amp;quot;), &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;optopt&lt;/span&gt;);
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;print&lt;/span&gt;_&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;usage&lt;/span&gt;(&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;stderr&lt;/span&gt;);
&lt;/span&gt;      &lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt; else &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-property-list syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;syntax-comment syntax-block syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;/*&lt;/span&gt; Special case for signal digit negative
         * PIDs &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;pid&lt;/span&gt; = (&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;long&lt;/span&gt;)(&amp;#39;0&amp;#39; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;optopt&lt;/span&gt;);
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;if&lt;/span&gt; (!&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;execute&lt;/span&gt;_&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;kill&lt;/span&gt;((&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;pid&lt;/span&gt;_&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;t&lt;/span&gt;) &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;pid&lt;/span&gt;, &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;signo&lt;/span&gt;, &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;use&lt;/span&gt;_&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;sigqueue&lt;/span&gt;, &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;sigval&lt;/span&gt;))
          &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;exitvalue&lt;/span&gt; = EXIT_FAILURE;
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;exit&lt;/span&gt;(&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;exitvalue&lt;/span&gt;);
&lt;/span&gt;      &lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;syntax-comment syntax-block syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;/*&lt;/span&gt; ... &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;
  }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My initial guess was wrong. There&#39;s no underflow or overflow bug with &lt;code&gt;-1490947&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Notice we&#39;re in the unrecognized option switch case. Here&#39;s what &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://man7.org/linux/man-pages/man3/getopt.3.html&quot;&gt;&lt;code&gt;getopt(3)&lt;/code&gt;&lt;/a&gt; says about unrecognized options:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;By default, &lt;code&gt;getopt()&lt;/code&gt; prints an error message on standard error, places the erroneous option character in optopt, and returns &lt;code&gt;&#39;?&#39;&lt;/code&gt; as the function result.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Aha. The &lt;code&gt;optopt&lt;/code&gt; variable only holds the first character after the dash, not the whole negative pid. When character &lt;code&gt;1&lt;/code&gt; is subtracted from character &lt;code&gt;0&lt;/code&gt; we get a pid of &lt;code&gt;-1&lt;/code&gt;. What happens when you kill pid &lt;code&gt;-1&lt;/code&gt;? &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://man7.org/linux/man-pages/man2/kill.2.html&quot;&gt;Bad things&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If pid equals -1, then sig is sent to every process for which the calling process has permission to send signals, except for process 1 (init).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This ran as root, so the misparsing of &lt;code&gt;-1490947&lt;/code&gt; as &lt;code&gt;-1&lt;/code&gt; killed every process but init. Bug found, mystery solved.&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alangrow.com/blog/a-decade-of-kill-bugs#wait-but-why&quot;&gt;Wait But Why&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Except for me this just raised more questions. Why is the code assuming the unrecognized option is a &amp;quot;signal [sic] digit negative PID&amp;quot;? Why is the single digit negative pid case special, and what handles the general case of a multidigit negative pid?&lt;/p&gt;
&lt;p&gt;Moreover, how could a bug like this exist in 2026, and in a Ubuntu LTS release no less? Has it always been there?&lt;/p&gt;
&lt;p&gt;No, that couldn&#39;t be. I&#39;ve killed process groups in the past. And the &lt;code&gt;kill -TERM -12345&lt;/code&gt; thing is an ancient Unix incantation. This used to work dammit!&lt;/p&gt;
&lt;p&gt;Thinking back, I could vaguely remember some negative pid kills gone awry. Never in an &amp;quot;oops this killed everything in sight&amp;quot; way. More in a &amp;quot;weird, nothing happened&amp;quot; way, or a &amp;quot;poof there goes my shell&amp;quot; way. Minor ways more easily shrugged off as user error.&lt;/p&gt;
&lt;p&gt;It turns out I wasn&#39;t imagining these. The last decade has been a rough one for negative pid killers. There have been no less than &lt;strong&gt;FOUR&lt;/strong&gt; distinct bugs around negative pids. And they all shipped in Ubuntu LTS versions.&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alangrow.com/blog/a-decade-of-kill-bugs#the-history&quot;&gt;The History&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before I tell the tale of four bugs, let&#39;s go even farther back in Unix history.&lt;/p&gt;
&lt;p&gt;In the beginning Unix had processes. By 1973 it had the &lt;code&gt;kill(1)&lt;/code&gt; program. But it took a decade to evolve process groups as we now know them. It wasn&#39;t until &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.tuhs.org/cgi-bin/utree.pl?file=V9/cmd/kill.c&quot;&gt;Unix V9 in 1986&lt;/a&gt; that you could kill a process group via negative pid. Near as I can tell, negative pid killing has worked fine in the BSDs all these years.&lt;/p&gt;
&lt;p&gt;In the early 90s this thing called Linux came along. Although Linux initially borrowed a lot of BSD code for its userland, by the late 90s this approach was in legal jeopardy. Everything had to be rewritten, every wheel reinvented. And quickly. It was an exciting time to be a unix programmer.&lt;/p&gt;
&lt;p&gt;And so it came to pass that Linux&#39;s &lt;code&gt;kill&lt;/code&gt; was reinvented written by &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lwn.net/Articles/851716/&quot;&gt;Albert Calahan&lt;/a&gt;. After Albert DoSed himself by forking 5 children, Debian downstream maintainer &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://dropbear.xyz/software/&quot;&gt;Craig Small&lt;/a&gt; took over poor old neglected &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gitlab.com/procps-ng/procps&quot;&gt;procps&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Negative pid killing seems to have worked fine until 2013. Then the trouble began.&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alangrow.com/blog/a-decade-of-kill-bugs#bug-1-ubuntu-14-cant-kill-1&quot;&gt;Bug 1: Ubuntu 14 Can&#39;t Kill -1&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ubuntu 14.04 LTS included procps 3.3.9. Prior to this, the &lt;code&gt;kill&lt;/code&gt; code did what the BSDs do and used pointer arithmetic to walk and parse &lt;code&gt;argv&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Personally I&#39;m fond of that approach. Stallman isn&#39;t so fond of it though. In GNU world, you&#39;re supposed to standardize command line parsing via &lt;code&gt;getopt&lt;/code&gt;. This is probably a good idea in the general case, but as we&#39;ll see, it was probably the wrong choice in the specific case of &lt;code&gt;kill(1)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&#39;s the relevant code after the conversion to &lt;code&gt;getopt&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;CSS&quot;&gt;&lt;span class=&quot;syntax-source syntax-css&quot;&gt;&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;opterr=0; &lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-block syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;/*&lt;/span&gt; suppress errors on -123 &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;while (loop == 1 &amp;amp;&amp;amp; (&lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-css&quot;&gt;i&lt;/span&gt; = getopt_long(argc, argv, &amp;quot;l&lt;span class=&quot;syntax-entity syntax-other syntax-pseudo-element syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-entity syntax-css&quot;&gt;::&lt;/span&gt;Ls&lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-other syntax-pseudo-class syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-entity syntax-css&quot;&gt;:&lt;/span&gt;hV&lt;/span&gt;&amp;quot;, longopts, NULL&lt;/span&gt;)) != -1)
&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;  &lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-css&quot;&gt;switch&lt;/span&gt; (&lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-css&quot;&gt;i&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;syntax-meta syntax-property-list syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-comment syntax-block syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;/*&lt;/span&gt; ... &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;case&lt;/span&gt; &amp;#39;?&amp;#39;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-key-value syntax-css&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-property-value syntax-css&quot;&gt;
      if (!isdigit(optopt&lt;/span&gt;)) {
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;xwarnx&lt;/span&gt;(_(&amp;quot;&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;invalid&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;argument&lt;/span&gt; %&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;c&lt;/span&gt;&amp;quot;), &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;optopt&lt;/span&gt;);
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;kill&lt;/span&gt;_&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;usage&lt;/span&gt;(&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;stderr&lt;/span&gt;);
&lt;/span&gt;      &lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;      loop=0;
      break;
    &lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-block syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;/*&lt;/span&gt; ... &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;
  }

&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;argc -= optind &lt;span class=&quot;syntax-punctuation syntax-separator syntax-combinator syntax-css&quot;&gt;+&lt;/span&gt; sigopt;
argv &lt;span class=&quot;syntax-punctuation syntax-separator syntax-combinator syntax-css&quot;&gt;+&lt;/span&gt;= optind;

for (&lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-css&quot;&gt;i&lt;/span&gt; = 0; &lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-css&quot;&gt;i&lt;/span&gt; &amp;lt; argc; &lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-css&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;syntax-invalid syntax-illegal syntax-combinator syntax-css&quot;&gt;++&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;syntax-meta syntax-property-list syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;pid&lt;/span&gt; = &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;strtol&lt;/span&gt;_&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;or&lt;/span&gt;_&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;err&lt;/span&gt;(&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;argv&lt;/span&gt;[&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;i&lt;/span&gt;], _(&amp;quot;&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;failed&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;parse&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;argument&lt;/span&gt;&amp;quot;));
  &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;if&lt;/span&gt; (!&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;kill&lt;/span&gt;((&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;pid&lt;/span&gt;_&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;t&lt;/span&gt;) &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;pid&lt;/span&gt;, &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;signo&lt;/span&gt;))
    &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;continue&lt;/span&gt;;
  &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;exitvalue&lt;/span&gt; = EXIT_FAILURE;
  &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code worked fine for multidigit negative pids. However, if you were one of those freaks creative sysadmins who delighted in a &lt;code&gt;kill -HUP -1&lt;/code&gt; now and again to restart all your services, you noticed a bug: nothing happened.&lt;/p&gt;
&lt;p&gt;The bug is due to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.matejka.ninja/software/argv.html#bundled-options&quot;&gt;option bundling&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Multidigit case&lt;/strong&gt;: With &lt;code&gt;kill -HUP -123&lt;/code&gt;, &lt;code&gt;getopt&lt;/code&gt; acts as if &lt;code&gt;-1&lt;/code&gt;, &lt;code&gt;-2&lt;/code&gt;, and &lt;code&gt;-3&lt;/code&gt; were all separately passed. The unrecognized option case will be hit three times, and only at &lt;code&gt;-3&lt;/code&gt; will the &lt;code&gt;-123&lt;/code&gt; argument be consumed by incrementing &lt;code&gt;optind&lt;/code&gt;. Because the code breaks out of the &lt;code&gt;getopt&lt;/code&gt; loop early at &lt;code&gt;-1&lt;/code&gt;, an unconsumed &lt;code&gt;-123&lt;/code&gt; argument correctly makes it to the pid killer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Single digit case&lt;/strong&gt;: With &lt;code&gt;kill -HUP -1&lt;/code&gt;, however, &lt;code&gt;getopt&lt;/code&gt; has fully consumed that argument and incremented &lt;code&gt;optind&lt;/code&gt; before we break out of the loop. The pid killer sees no arguments and nothing happens.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alangrow.com/blog/a-decade-of-kill-bugs#bug-2-ubuntu-16-accidentally-kills-1&quot;&gt;Bug 2: Ubuntu 16 Accidentally Kills -1&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A cure was in order. Unfortunately it was worse than the disease.&lt;/p&gt;
&lt;p&gt;Ubuntu 16.04 LTS shipped with procps 3.3.10. The &lt;code&gt;kill -HUP -1&lt;/code&gt; bug was fixed at the expense of multidigit negative pid kills: a &lt;code&gt;kill -TERM -12345&lt;/code&gt; now killed everything.&lt;/p&gt;
&lt;p&gt;This is the same bug that caused the outage under Ubuntu 24. We reviewed this &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alangrow.com/blog/a-decade-of-kill-bugs#the-bug&quot;&gt;above&lt;/a&gt;. The only mystery here is why procps circa Ubuntu 24 resurrected the known-buggy code circa Ubuntu 16. My best guess is that the following bugs were deemed even worse.&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alangrow.com/blog/a-decade-of-kill-bugs#bug-3-ubuntu-18-segfaults-killing-1&quot;&gt;Bug 3: Ubuntu 18 Segfaults Killing -1&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ubuntu 18.04 LTS shipped with procps 3.3.12.&lt;/p&gt;
&lt;p&gt;This attempted to fix multidigit negative pid killing. Instead of parsing &lt;code&gt;optopt&lt;/code&gt;, which only corresponds to a single bundled character like &lt;code&gt;-1&lt;/code&gt; when &lt;code&gt;-123&lt;/code&gt; is passed, the new code stops and tries to parse the whole argument:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;CSS&quot;&gt;&lt;span class=&quot;syntax-source syntax-css&quot;&gt;&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;opterr=0; &lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-block syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;/*&lt;/span&gt; suppress errors on -123 &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;while (loop == 1 &amp;amp;&amp;amp; (&lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-css&quot;&gt;i&lt;/span&gt; = getopt_long(argc, argv, &amp;quot;l&lt;span class=&quot;syntax-entity syntax-other syntax-pseudo-element syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-entity syntax-css&quot;&gt;::&lt;/span&gt;Ls&lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-other syntax-pseudo-class syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-entity syntax-css&quot;&gt;:&lt;/span&gt;hV&lt;/span&gt;&amp;quot;, longopts, NULL&lt;/span&gt;)) != -1)
&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;  &lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-css&quot;&gt;switch&lt;/span&gt; (&lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-css&quot;&gt;i&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;syntax-meta syntax-property-list syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-comment syntax-block syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;/*&lt;/span&gt; ... &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;case&lt;/span&gt; &amp;#39;?&amp;#39;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-key-value syntax-css&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-property-value syntax-css&quot;&gt;
      if (!isdigit(optopt&lt;/span&gt;)) {
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;xwarnx&lt;/span&gt;(_(&amp;quot;&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;invalid&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;argument&lt;/span&gt; %&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;c&lt;/span&gt;&amp;quot;), &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;optopt&lt;/span&gt;);
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;kill&lt;/span&gt;_&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;usage&lt;/span&gt;(&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;stderr&lt;/span&gt;);
&lt;/span&gt;      &lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt; else &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-property-list syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;syntax-comment syntax-block syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;/*&lt;/span&gt; Special case for signal digit negative
         * PIDs &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;pid&lt;/span&gt; = &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;atoi&lt;/span&gt;(&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;argv&lt;/span&gt;[&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;optind&lt;/span&gt;]);
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;kill&lt;/span&gt;((&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;pid&lt;/span&gt;_&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;t&lt;/span&gt;)&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;pid&lt;/span&gt;, &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;signo&lt;/span&gt;) != 0)
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;exitvalue&lt;/span&gt; = EXIT_FAILURE;
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;exit&lt;/span&gt;(&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;exitvalue&lt;/span&gt;);
&lt;/span&gt;      &lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;      loop=0;
      break;
    &lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-block syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;/*&lt;/span&gt; ... &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;
  }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works correctly for multidigit negative pids. But if you passed a single digit negative pid, as in a &lt;code&gt;kill -TERM -1&lt;/code&gt;, the pid argument was fully consumed, and &lt;code&gt;optind&lt;/code&gt; would run off the end of &lt;code&gt;argv[]&lt;/code&gt;. Segfault!&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alangrow.com/blog/a-decade-of-kill-bugs#bug-4-ubuntu-22-kills-your-shell&quot;&gt;Bug 4: Ubuntu 22 Kills Your Shell&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ubuntu 22.04 LTS shipped with procps 3.3.17.&lt;/p&gt;
&lt;p&gt;This fixed the segfault by ensuring &lt;code&gt;optind&lt;/code&gt; never ran off the end of &lt;code&gt;argv[]&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;CSS&quot;&gt;&lt;span class=&quot;syntax-source syntax-css&quot;&gt;&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;opterr=0; &lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-block syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;/*&lt;/span&gt; suppress errors on -123 &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;while ((&lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-css&quot;&gt;i&lt;/span&gt; = getopt_long(argc, argv, &amp;quot;l&lt;span class=&quot;syntax-entity syntax-other syntax-pseudo-element syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-entity syntax-css&quot;&gt;::&lt;/span&gt;Ls&lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-other syntax-pseudo-class syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-entity syntax-css&quot;&gt;:&lt;/span&gt;hVq&lt;/span&gt;:&amp;quot;, longopts, NULL&lt;/span&gt;)) != -1)
&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;  &lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-css&quot;&gt;switch&lt;/span&gt; (&lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-css&quot;&gt;i&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;syntax-meta syntax-property-list syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-comment syntax-block syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;/*&lt;/span&gt; ... &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;case&lt;/span&gt; &amp;#39;?&amp;#39;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-key-value syntax-css&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-property-value syntax-css&quot;&gt;
      if (!isdigit(optopt&lt;/span&gt;)) {
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;xwarnx&lt;/span&gt;(_(&amp;quot;&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;invalid&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;argument&lt;/span&gt; %&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;c&lt;/span&gt;&amp;quot;), &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;optopt&lt;/span&gt;);
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;kill&lt;/span&gt;_&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;usage&lt;/span&gt;(&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;stderr&lt;/span&gt;);
&lt;/span&gt;      &lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt; else &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-property-list syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;syntax-comment syntax-block syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;/*&lt;/span&gt; Special case for signal digit negative
         * PIDs &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;pid&lt;/span&gt; = &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;atoi&lt;/span&gt;(&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;argv&lt;/span&gt;[&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;optind-&lt;/span&gt;1]);
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;kill&lt;/span&gt;((&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;pid&lt;/span&gt;_&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;t&lt;/span&gt;)&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;pid&lt;/span&gt;, &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;signo&lt;/span&gt;) != 0)
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;exitvalue&lt;/span&gt; = EXIT_FAILURE;
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;exit&lt;/span&gt;(&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;exitvalue&lt;/span&gt;);
&lt;/span&gt;      &lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;      xerrx(EXIT_FAILURE, _(&amp;quot;internal error&amp;quot;&lt;/span&gt;));
    &lt;span class=&quot;syntax-comment syntax-block syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;/*&lt;/span&gt; ... &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-css&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;
  }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once again, it seems the contributor didn&#39;t understood how &lt;code&gt;getopt&lt;/code&gt; option bundling interacts with &lt;code&gt;optind&lt;/code&gt;, and hopefully by this point you&#39;re developing some sympathy for them. With this patch a &lt;code&gt;kill -TERM -1&lt;/code&gt; works correctly and no longer segfaults. But multidigit negative pid killing was broken for a second time, in a brand new way.&lt;/p&gt;
&lt;p&gt;Suppose we try to &lt;code&gt;kill -TERM -123&lt;/code&gt;. Thanks to option bundling, &lt;code&gt;optind&lt;/code&gt; isn&#39;t incremented yet when we hit the unrecognized option case. Per some &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alangrow.com/blog/a-decade-of-kill-bugs#oh-were-halfway-there&quot;&gt;quirky behavior noted below&lt;/a&gt;, &lt;code&gt;argv[optind-1]&lt;/code&gt; is now the string &lt;code&gt;kill&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;How does &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://man7.org/linux/man-pages/man3/atoi.3.html&quot;&gt;&lt;code&gt;atoi(3)&lt;/code&gt;&lt;/a&gt; handle a non-numeric string like &lt;code&gt;&amp;quot;kill&amp;quot;&lt;/code&gt;? It returns &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And what does the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://man7.org/linux/man-pages/man2/kill.2.html&quot;&gt;kill&lt;/a&gt; system call do with zero?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If pid equals 0, then sig is sent to every process in the process group of the calling process.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Because it&#39;s a simple command, your shell won&#39;t isolate &lt;code&gt;kill -TERM -123&lt;/code&gt; in a new process group. So this bug will unexpectedly kill your shell. Poof!&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alangrow.com/blog/a-decade-of-kill-bugs#what-did-we-learn-palmer&quot;&gt;What Did We Learn, Palmer?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are several takeaways.&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alangrow.com/blog/a-decade-of-kill-bugs#the-narrow-learning&quot;&gt;The Narrow Learning&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For one, the way &lt;code&gt;getopt&lt;/code&gt; only advances &lt;code&gt;optind&lt;/code&gt; with the final bundled option is deeply non-obvious. I didn&#39;t find evidence that any contributor understood this. I didn&#39;t understand it myself at first. That took careful study.&lt;/p&gt;
&lt;p&gt;It&#39;s a real surprise. Surprising behavior like this should be avoided.&lt;/p&gt;
&lt;p&gt;I feel this learning is too myopic though. Can one even avoid this surprising &lt;code&gt;getopt&lt;/code&gt; behavior? You can&#39;t disable option bundling.&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alangrow.com/blog/a-decade-of-kill-bugs#the-broader-learning&quot;&gt;The Broader Learning&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Getopt Considered Harmful.&lt;/p&gt;
&lt;p&gt;Well okay, at least in this specific case. The &lt;code&gt;kill(1)&lt;/code&gt; program is just an unusual beast. It accepts options that are actually signal names, like &lt;code&gt;-KILL&lt;/code&gt;. You can also specify that same signal by number, like &lt;code&gt;-9&lt;/code&gt;. And pgids are negative numbers that look deceptively like options.&lt;/p&gt;
&lt;p&gt;How should a command line like &lt;code&gt;kill -1 -9&lt;/code&gt; be interpreted?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Should it send SIGHUP to pgid 9?&lt;/li&gt;
&lt;li&gt;Should it send SIGKILL to pgid 1, aka everything?&lt;/li&gt;
&lt;li&gt;Should it send SIGTERM, the default signal, to pgids 1 and 9?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these interpretations are potentially valid if any &lt;code&gt;argv&lt;/code&gt; element can be an option or a pid.&lt;/p&gt;
&lt;p&gt;The solution is to instead parse &lt;code&gt;argv&lt;/code&gt; positionally. Don&#39;t use &lt;code&gt;getopt&lt;/code&gt; here. Abandon capabilities like option bundling and option reordering that are more footgun than feature in this context. The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://pubs.opengroup.org/onlinepubs/9699919799/utilities/kill.html&quot;&gt;POSIX spec&lt;/a&gt; says that only &lt;code&gt;argv[1]&lt;/code&gt; can specify the signal to send, and arguments beyond that are pids and pgids. These are nice simplifying assumptions.&lt;/p&gt;
&lt;p&gt;I think the old-school Unix and BSD approach is the right one. Have a look at FreeBSD&#39;s &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/freebsd/freebsd-src/blob/bd4975e5fb051c2b2e8d5218940b7e8ef87b5bdf/bin/kill/kill.c#L53-L136&quot;&gt;kill.c&lt;/a&gt;. Like ancestral Unix did back in the 70s, it parses &lt;code&gt;argv&lt;/code&gt; positionally. The approach is simple and the code is simple. By contrast with Linux&#39;s &lt;code&gt;kill&lt;/code&gt;, FreeBSD&#39;s &lt;code&gt;kill&lt;/code&gt; has had no command line parsing bugs.&lt;/p&gt;
&lt;p&gt;I do regard &lt;code&gt;getopt&lt;/code&gt; dogma as the real root cause of the outage.&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alangrow.com/blog/a-decade-of-kill-bugs#oh-were-halfway-there&quot;&gt;Oh, We&#39;re Halfway There&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If abandoning &lt;code&gt;getopt&lt;/code&gt; for &amp;quot;primitive&amp;quot; &lt;code&gt;argv&lt;/code&gt; parsing still feels sinful, just look at what procps does before even calling &lt;code&gt;getopt_long&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;if (argc &amp;lt; 2)
  print_usage(stderr);

signo = skill_sig_option(&amp;amp;argc, argv);
if (signo &amp;lt; 0)
  signo = SIGTERM;

opterr=0; /* suppress errors on -123 */
while ((i = getopt_long(argc, argv, &amp;quot;l::Ls:hVq:&amp;quot;, longopts, &amp;amp;optindex)) != -1)
/* ... */
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What does &lt;code&gt;skill_sig_option&lt;/code&gt; do?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;CSS&quot;&gt;&lt;span class=&quot;syntax-source syntax-css&quot;&gt;&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;int skill_sig_option(int &lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-wildcard syntax-css&quot;&gt;*&lt;/span&gt;argc, char &lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-wildcard syntax-css&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-tag syntax-wildcard syntax-css&quot;&gt;*&lt;/span&gt;argv&lt;/span&gt;)
&lt;span class=&quot;syntax-meta syntax-property-list syntax-css&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;i&lt;/span&gt;;
  &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;signo&lt;/span&gt; = &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;-&lt;/span&gt;1;
  &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;i&lt;/span&gt; = 1; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;i&lt;/span&gt; &amp;lt; *&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;argc&lt;/span&gt;; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;i&lt;/span&gt;++) {
    &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;argv&lt;/span&gt;[&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;i&lt;/span&gt;][0] == &amp;#39;&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;-&lt;/span&gt;&amp;#39;) {
      &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;signo&lt;/span&gt; = &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;signal&lt;/span&gt;_&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;name&lt;/span&gt;_&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;to&lt;/span&gt;_&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;number&lt;/span&gt;(&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;argv&lt;/span&gt;[&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;i&lt;/span&gt;] + 1);
      &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;-&lt;/span&gt;1 &amp;lt; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;signo&lt;/span&gt;) {
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;memmove&lt;/span&gt;(&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;argv&lt;/span&gt; + &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;i&lt;/span&gt;, &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;argv&lt;/span&gt; + &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;i&lt;/span&gt; + 1,
            &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;sizeof&lt;/span&gt;(&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;char&lt;/span&gt; *) * (*&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;argc&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;i&lt;/span&gt;));
        (*&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;argc&lt;/span&gt;)&lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;--&lt;/span&gt;;
        &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-property-name syntax-css&quot;&gt;signo&lt;/span&gt;;
&lt;/span&gt;      &lt;span class=&quot;syntax-punctuation syntax-section syntax-property-list syntax-css&quot;&gt;}&lt;/span&gt;
    }
  }
&lt;span class=&quot;syntax-meta syntax-selector syntax-css&quot;&gt;  return signo;
}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It scans &lt;code&gt;argv&lt;/code&gt; looking for the first signal spec. If it finds a &lt;code&gt;-KILL&lt;/code&gt;, a &lt;code&gt;-9&lt;/code&gt;, or similar, it modifies &lt;code&gt;argv&lt;/code&gt; in place (!) so &lt;code&gt;getopt&lt;/code&gt; doesn&#39;t have to deal with that.&lt;/p&gt;
&lt;p&gt;This is already &amp;quot;primitive&amp;quot; &lt;code&gt;argv&lt;/code&gt; parsing! We&#39;re halfway there.&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alangrow.com/blog/a-decade-of-kill-bugs#some-final-practical-advice&quot;&gt;Some Final Practical Advice&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The previous takeaways were aimed at software developers. For users of &lt;code&gt;kill&lt;/code&gt;, I&#39;d recommend the following.&lt;/p&gt;
&lt;p&gt;First, use &lt;code&gt;--&lt;/code&gt; to separate options from pids and pgids:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;kill -TERM -1490947     # no
kill -TERM -- -1490947  # yes
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second form is parsed correctly even by the buggy procps versions listed above.&lt;/p&gt;
&lt;p&gt;Second, kill with least privilege. If I&#39;d done a &lt;code&gt;sudo -u render kill&lt;/code&gt; instead of a &lt;code&gt;sudo kill&lt;/code&gt;, it would have only killed in-flight renders instead of all processes on the box. I feel a little insane recommending this kind of defensive tool usage — let&#39;s be clear, we&#39;re defending against bugs in &lt;code&gt;kill&lt;/code&gt;, a small, mature, widely-used and very important tool — but here we are.&lt;/p&gt;
&lt;p&gt;Finally, the good news is that a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lists.ubuntu.com/archives/ubuntu-sponsors/2026-September/088746.html&quot;&gt;bugfix for Ubuntu 24&lt;/a&gt; is already on its way at the time of publication. I still favor a simplifying rewrite of procps&#39;s &lt;code&gt;kill&lt;/code&gt; though.&lt;/p&gt;
</content>
  </entry>
</feed>
