ときどきの雑記帖 RE* (新南口)
カス人間第一号
今週のしずえさん
「定礎」
定礎板の中には何かが収められている(ことが多い)らしい。
25th
今年で C++ Builder 25 周年です。25 周年記念サイトや、記念 Blog 記事があったりしてなかなか盛り上がっているようです。
25年かあ……(遠い目)
C++ Builder って出る前や最初にリリースされたときはかなり期待が大きかったと思うんだけど、 Delphiほどには盛り上がらなかった(という印象を持っている)のは記法のせいもあるんですかねえ?
__closure
とか「面白い」拡張もあったなあ。
This subsubsection has been reported to cause headaches. You might want to skip it upon first reading.
さて、では & という文字に置換したい場合はどうすればいいでしょうか?⇒ \& という文字列にします。 ただし \ という文字を使うためにはエスケープしなければいけないので・・・
以前にも書いたような気がするけど(調べない)、
gawkのリファレンスマニュアル(Info)でも結構な分量が割かれて説明されているくらい
闇の部分の話なのであった>\&
の置換問題
9.1.3.1 More about ‘\’ and ‘&’ with sub(), gsub(), and gensub()
descripter 10
以前、 シェルがスクリプトをどう読みこんでいるか という、世間でその頃盛り上がっていた話題に便乗した記事を書いたのだけど、 その最後の方 で
ファイルディスクリプタをdupするときにわざわざ(?)その結果を10以上にしていること。 これはbashなどでも踏襲されているのでなにか目的(と理由)があってのことだとは思うのだけど、 「これだ」と断言できるだけの情報がない。
ということを書いていた。 これに関しての追加報告(?)
ふと思いついて、unix file descriptor 10
のようなキーワードで検索したら
いくつか興味深いものが見つかった。
たとえば
What is file descriptor 10?
Real-world example (where standard output is ignored, standard error redirected to standard output and data from file descriptor 10 is redirected to standard output) that I have lost the documentation/context for:
や
When the script runs, a file descriptor 10 appears with the contents of the executable script.
For example, the script:
#!/bin/sh sleep 600
When viewing the fd of a running script through processes, I see that fd10 appeared, with the contents of the executable script itself. Why?
など。 後者の方の先を読んでみるとこんなことが書かれている。
The shell reads and executes the script one statement at a time, rather than reading the entire script at startup. Therefore it needs to keep a connection to the script.
If the question is “why 10”?, then it is essentially this is “the first FD greater than 9”. Historically the shell allowed you to manipulate the single digits file descriptors, e.g. ’exec 4>/dev/tty`. Modern shell’s allow you to manipulate FD greater than 9.
Of course it could read everything at startup, but that would mean you couldn’t usefully give things like named pipes or devices as the “script”.
なるほど。これだけではまだ根拠にするには弱い気もするけど、 正規表現の back reference も一桁限定だったりしたから ありそうな話ではある。
そして改めてBashのリファレンスマニュアルを読んでみると
Each redirection that may be preceded by a file descriptor number may instead be preceded by a word of the form {varname}. In this case, for each redirection operator except >&- and <&-, the shell will allocate a file descriptor greater than 10 and assign it to {varname}. If >&- or <&- is preceded by {varname}, the value of varname defines the file descriptor to close. If {varname} is supplied, the redirection persists beyond the scope of the command, allowing the shell programmer to manage the file descriptor’s lifetime manually.
という記述があった。確かにallocate a file descriptor greater than 10 and assign it
なことをしている。
execve("/usr/bin/sh", ["sh", "-c", "ls foo 3>&2 2>&1 1>&3"], 0x7fffde1123e8 /* 21 vars */) = 0
close(3) = 0
fcntl(3, F_DUPFD, 10) = -1 EBADF (Bad file descriptor)
dup2(2, 3) = 3
fcntl(2, F_DUPFD, 10) = 10
close(2) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(1, 2) = 2
fcntl(1, F_DUPFD, 10) = 11
close(1) = 0
fcntl(11, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
clone(strace: Process 180 attached
child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fdda46e1850) = 180
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=180, si_uid=1000, si_status=2, si_utime=0, si_stime=0} ---
rt_sigreturn({mask=[]}) = 180
dup2(11, 1) = 1
close(11) = 0
dup2(10, 2) = 2
close(10) = 0
close(3) = 0
exit_group(2) = ?
+++ exited with 2 +++
(straceの結果ですが関係なさそうなところは省いています)
Redirections using file descriptors greater than 9 should be used with care, as they may conflict with file descriptors the shell uses internally.
そしてこういう注意書きも。
じゃあopengroupでは…というと
Open files are represented by decimal numbers starting with zero. The largest possible value is implementation-defined; however, all implementations shall support at least 0 to 9, inclusive, for use by the application. These numbers are called “file descriptors”. The values 0, 1, and 2 have special meaning and conventional uses and are implied by certain redirection operations; they are referred to as standard input, standard output, and standard error, respectively. Programs usually take their input from standard input, and write output on standard output. Error messages are usually written on standard error. The redirection operators can be preceded by one or more digits (with no intervening <blank> characters allowed) to designate the file descriptor number.
最低サポートしなければならないfile descriptorとして0~9と。
そしてv7のシェルをみると
v7/sh/word.c の関数wordでは
IF argp->argval[1]==0 ANDF (d=argp->argval[0], digit(d)) ANDF (c=='>' ORF c=='<')
THEN word(); wdnum=d-'0';
ELSE /*check for reserved words*/
IF reserv==FALSE ORF (wdval=syslook(argp->argval,reserved))==0
THEN wdarg=argp; wdval=0;
FI
FI
と、一桁の数字しか考慮してないすな。
service.cの関数initioでも10以上かチェックして、そうなら蹴ると。
ELIF iof&IOMOV
THEN IF eq(minus,ion)
THEN fd = -1;
close(iof&IOUFD);
ELIF (fd=stoi(ion))>=USERIO
THEN failed(ion,badfile);
ELSE fd=dup(fd);
FI
Modernize
Linuxカーネルの記述言語をC11に移行するとかいう話題で。
Linus Torvaldsは5.18からLinuxをC11へ移行する計画。現在、LinuxはC89という古い規格を用いている。C99への移行計画が存在したが、最近のセキュリティ上の問題のpatchにおいてC99では問題の存在が判明。このためC99をパスしてC11にまで移行することに。 https://t.co/UI1Worc7lr
— 高梨陣平 (@jingbay) February 27, 2022
高梨さんのこの文章だと C11にすることでC99では存在する問題がなくなる、 解決される(のでC11に移行する)ように読める(と思う)のだけど、 C99からC11での変更点と今回問題になっていることとを見ても
C99の問題って何だろうと思って元々のスレッドを見に行ったけど単に「GCC 5.1以降ではC11もサポートされているから一気にC11に移行しよう」という話に見えた
— mod_poppo (@mod_poppo) February 28, 2022
という意見もあるように、実際のところはそう (C11にすれば解決できる)ではないよね?
メーリングリストのやりとりも結構長いし追いかけきれてないのだけど、
発端となったバグの原因の一つには
for (int counter=0; counter<limit; ++counter)
みたいな
(変数のスコープがforに限定される)
変数宣言ができないという話があって、
でもそれはC99で導入済みのわけですよね。
じゃあC99での問題ってのはどういうことなのよと追いかけると
Linus Torvalds Prepares to Move the Linux Kernel to Modern C https://t.co/irFUwKwRI8
— Slashdot (@slashdot) February 27, 2022
Linus Torvalds Prepares to Move the Linux Kernel to Modern C - Slashdot という./のスレッドの先頭に
Linus Torvalds has decided that Linux will move to the C11 standard starting with kernel 5.18…. Linux had planned to move to a newer standard eventually with C99 being the next version. However a recent patch to a security problem revealed that there could be problems with C99.
In order to patch a potential security problem with Linux’s linked-list primitive speculative-execution functions, it was found that C99 would require the iterator must be declared outside the loop which would expose it to another security problem. Since C99 was not very popular, it was agreed to skip it and use C11. Backwards compatibility with most compilers like gcc should allow for an easily transition of most of the code.
とあった。最初のパラグラフの最後の文、However以下をみて「C99では問題の存在が判明」
としたのかな。
で、その「問題」(のひとつ)が
require the iterator must be declared outside the loop
なのだろうけど、C11にしたからって解決しないよね。それ。
Re: [RFC PATCH 03/13] usb: remove the usage of the list iterator after the loop [LWN.net]
In fact, I think the change to list_for_each_entry() should have been done not as “fix type speculation”, but as a much more interesting “fix the list iterators”.
The whole reason this kind of non-speculative bug can happen is that we historically didn’t have C99-style “declare variables in loops”. So list_for_each_entry() - and all the other ones - fundamentally always leaks the last HEAD entry out of the loop, simply because we couldn’t declare the iterator variable in the loop itself.
Linus Torvalds prepares to move the Linux kernel to modern C | ZDNet
While fixing this, Torvalds realized that in C99 the iterator passed to the list-traversal macros must be declared in a scope outside of the loop itself.
Since C99 was never that popular and C11 introduced standardized multithreading support and made the language a trifle safer this sounds like a good move.
Moving the kernel to modern C [LWN.net]
The list member can be used to create a doubly-linked list of foo structures; a separate list_head structure is usually declared as the beginning of such a list; assume we have one called foo_list. Traversing this list is possible with code like:
struct foo *iterator;
list_for_each_entry(iterator, &foo_list, list) {
do_something_with(iterator);
}
/* Should not use iterator here */
list.h - include/linux/list.h - Linux source code (v5.17-rc5) - Bootlin
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member); \
!list_entry_is_head(pos, head, member); \
pos = list_next_entry(pos, member))
確かにこのposをfor文に閉じ込めれば…て話ではあるのだけど、じゃあその型は?
gccのtypeof
でも持ってこないと書けないような気がするんだがなあ。
あと、このほかにもリスト関連のマクロがいくつもあって、
中には展開されたfor文でposに初期値を設定してないものがあったりするわけですよ。
もーなんやらよくわからん(投げる)。
結局、C89から(C99でもなくC17やC23でもない)C11へ移行する理由は Linus Torvalds氏、LinuxカーネルをモダンなCに移行する準備中 | ソフトアンテナ にあるように
Linus氏はバグが、C99スタイルの「ループ内での変数宣言」がなかったからだと指摘し、C89からこの種の問題が発生しない、 より新しい標準Cへの移行の検討が始まります。
Linuxカーネル開発者のArnd Bergmann氏も移行に同意し、C99よりもさらに新しい2011年に発表されたC11規格に移行することも 可能なはずだとコメントしています。
ということで、ここで「C11への移行も可能なはず」というのは、
Linuxカーネルの最小要件のCコンパイラであるGCC version 5.1もC11をサポートしているからです。
だからということだろうと。
と、ここまで書いていたら後続の記事で
zdnet
トーバルズ氏、Linuxカーネルを「C89」から「C11」コードに移行する準備 - ZDNet Japan
その答えは、いよいよC89に別れを告げ、この種の問題が起こり得ないCの新規格に移行するというものだ。 つまり「C99規格への移行を検討する時期が来たということだ。これは20年以上も前に策定された規格だが、 少なくともブロックレベルでの変数宣言が可能な程度には新しいもの」となっている。
なんかここでも 「この種の問題が起こり得ないCの新規格に移行」 とか書かれてるし。
原文そんなの書いてたっけ? と比較すると
Linus Torvalds prepares to move the Linux kernel to modern C | ZDNet
The answer? Finally, move from C89 to a newer standard C that makes this kind of problem can’t occur.
So, “the time had come to look at moving to the C99 standard — it is still over 20 years old, but is at least recent enough to allow block-level variable declarations.”
うーん……ここのSo を「つまり」と訳していいのかなあ?
それにその後も the time had come
だから現在形じゃないし(未来のことを指しているわけでもない)。
この前の部分でも
同氏はLinuxカーネルメーリングリスト(LKML)に「この種の非投機的なバグが発生する理由は、C99スタイルの『ループ内での変数宣言』 という選択肢をわれわれが今まで持ち合わせてこなかったためにほかならない。つまり、list_for_each_entry()といったものすべては基本的に常に、 最後のHEADエントリーをループ外にリークさせる。というのも、ループ本体内でイテレーター変数を宣言できないためだ」と記している。
Torvalds氏がこうした考えに至ったきっかけは、カーネルのリンクリストプリミティブに関連する投機的実行の脆弱性を 解決するパッチを適用しようとした際に、該当パッチが持つ別の問題が明らかになったことだった。同氏はこの問題を修正するには、 「C99」でリストのトラバースマクロに引き渡すイテレーターを、ループ本体の外のスコープで宣言しておかなければならないという点に気付いた。
Torvalds wrote to the Linux Kernel Mailing List (LKML) that “the whole reason this kind of non-speculative bug can happen is that we historically didn’t have C99-style ‘declare variables in loops.” So list_for_each_entry() - and all the other ones - fundamentally always leaks the last HEAD entry out of the loop, simply because we couldn’t declare the iterator variable in the loop itself.”
The situation came to Torvald’s attention when, in order to patch a potential security problem with the kernel’s linked-list primitive speculative-execution functions, another problem was revealed in the patch. While fixing this, Torvalds realized that in C99 the iterator passed to the list-traversal macros must be declared in a scope outside of the loop itself.
繰り返しになるけど、C99で根本的に解決できないのはともかく C11にしたからって解決できないよね、それ。 「この種の問題が起こり得ないCの新規格に移行」ということならなんか変じゃない?
links
- トーバルズ氏、Linuxカーネルを「C89」から「C11」コードに移行する準備 - ZDNet Japan
- Moving the kernel to modern C [LWN.net]
- Linus Torvalds prepares to move the Linux kernel to modern C : programming
- Linus Torvalds prepares to move the Linux kernel to modern C | ZDNet
- Linus Torvalds Prepares to Move the Linux Kernel to Modern C - Slashdot
- Moving the Linux Kernel to Modern C | Hacker News
- Moving the kernel to modern C [LWN.net]
- Linus Torvalds氏、LinuxカーネルをモダンなCに移行する準備中 | ソフトアンテナ
- [RFC PATCH 00/13] Proposal for speculative safe list iterator [LWN.net]
- [RFC PATCH 02/13] scripts: coccinelle: adapt to find list_for_each_entry nospec issues [LWN.net]
- Re: [RFC PATCH 03/13] usb: remove the usage of the list iterator after the loop [LWN.net]
- [B! linux] トーバルズ氏、Linuxカーネルを「C89」から「C11」コードに移行する準備
- C11 (C言語) - Wikipedia
- C11 (C standard revision) - Wikipedia
メモ
Hugoメモ
使っているバージョンを0.93.0に上げたら上げたで 即座にバグフィックスのリリースが
This is a bug-fix release with a couple of important fixes.
tpl/os: Revert readDir in theme behaviour 673cde1 @bep #9599
markup/goldmark: Escape image alt attribute e46e9ce @jmooring #9594
This is a bug-fix release with a couple of important fixes.
tpl/transform: Fix it when template.HTML is passes as option to Hightlight 0327da0 @bep #9591
tpl/partials: Fix partialCached deadlock regression 9b8b6d3 @bep #9588
tpl/collections: Fix apply when function have Context as first arg 376704d @bep #9585
ん-、影響はなさそう?
onceプラグマ, defer, ラムダ, 多重loopからのbreak, constexprなどは C23入りはしないとのこと っつーか"break break"とかセンスが...(個人の見解です
— yoh (@yohhoy) February 28, 2022