Unicode

Heirloom Toolchest tr:嘗試刪除包含多字節字元的集合的補碼時出錯?

  • June 4, 2014

我正在嘗試使用Heirloom Toolchesttr中的命令來克服coreutils實現的目前限制,以便能夠從“隨機”生成器(/dev/urandom) “抽取”(使用選項)多字節字元到終端。值得注意的是,在使用 AUR版本失敗後,這是從Archbang上的原始碼編譯的。-dc

為了簡化這一點,讓我們選擇一個字元(☠)併計算出它的八進制值,因為這是它必須為 toolchest 表示的方式tr

echo '☠' | hexdump -b            # -b for octal
0000000 342 230 240 012                                                
0000004
echo -e '\0342\0230\0240'        # uses the "0nnn" format, make sure it prints
☠

與此處的工具箱(nnn )相比,使用內置函式(0nnn )在**Bash中表示八進制值的方式有所不同:echo tr

字元 ‘' 後跟 1、2 或 3 個八進制數字代表其字節碼由這些數字給出的字元。多字節字元可以指定為八進製字節序列。

讓我們試試看。該-dc選項只是刪除 SET1 的補碼。您指定一個集合,標準輸入中不包含集合中元素的任何內容都會被丟棄:

echo '012345' | /usr/5bin/tr -dc '456'   #sanity check
45                                       #all good

現在這些:

echo -e '\0342\0230\0240' | /usr/5bin/tr -dc '\342\230\240'
echo -e '☠' | /usr/5bin/tr -dc '☠'

它應該都列印一個(1)☠,或者最終以下(更多字元)都會產生相同的錯誤:

/usr/5bin/tr -dc '\342\230\240' < /dev/urandom

*** Error in `/usr/5bin/tr': double free or corruption (!prev): 0x0000000000d24420 ***

實際上,每次輸入和 SET1 都包含所選字元時,錯誤會以-dc. *在工具箱中提供的命令的SysV 3rd、4th、Posix、Posix2001 或 ucb(BSD)*版本中,該行為也是相同的。有時,就像tr -dc '1' < /dev/urandom我得到一個適當的段錯誤或幾行輸出的情況一樣:

Error in `/usr/5bin/tr': realloc(): invalid pointer: 0x00007f93ee284010 ***
======= Backtrace: =========
/usr/lib/libc.so.6(+0x73f8e)[0x7f93ee338f8e]
/usr/lib/libc.so.6(+0x7988e)[0x7f93ee33e88e]
/usr/lib/libc.so.6(realloc+0x1c8)[0x7f93ee342918]
/usr/5bin/tr[0x401a74]
/usr/5bin/tr[0x400e93]
/usr/lib/libc.so.6(__libc_start_main+0xf0)[0x7f93ee2e5000]
/usr/5bin/tr[0x400f63]
======= Memory map: ========
00400000-00403000 r-xp 00000000 08:21 1579535                            /usr/5bin/tr
00602000-00603000 rw-p 00002000 08:21 1579535                            /usr/5bin/tr
0067a000-006bc000 rw-p 00000000 00:00 0                                  [heap]
7f93edc6e000-7f93edc84000 r-xp 00000000 08:21 1448153                    /usr/lib/libgcc_s.so.1
7f93edc84000-7f93ede83000 ---p 00016000 08:21 1448153                    /usr/lib/libgcc_s.so.1
7f93ede83000-7f93ede84000 rw-p 00015000 08:21 1448153                    /usr/lib/libgcc_s.so.1
7f93ede84000-7f93ee2c5000 rw-p 00000000 00:00 0 
7f93ee2c5000-7f93ee469000 r-xp 00000000 08:21 1440453                    /usr/lib/libc-2.19.so
7f93ee469000-7f93ee669000 ---p 001a4000 08:21 1440453                    /usr/lib/libc-2.19.so
7f93ee669000-7f93ee66d000 r--p 001a4000 08:21 1440453                    /usr/lib/libc-2.19.so
7f93ee66d000-7f93ee66f000 rw-p 001a8000 08:21 1440453                    /usr/lib/libc-2.19.so
7f93ee66f000-7f93ee673000 rw-p 00000000 00:00 0 
7f93ee673000-7f93ee694000 r-xp 00000000 08:21 1440340                    /usr/lib/ld-2.19.so
7f93ee6eb000-7f93ee874000 r--p 00000000 08:21 1448356                    /usr/lib/locale/locale-archive
7f93ee874000-7f93ee877000 rw-p 00000000 00:00 0 
7f93ee891000-7f93ee893000 rw-p 00000000 00:00 0 
7f93ee893000-7f93ee894000 r--p 00020000 08:21 1440340                    /usr/lib/ld-2.19.so
7f93ee894000-7f93ee895000 rw-p 00021000 08:21 1440340                    /usr/lib/ld-2.19.so
7f93ee895000-7f93ee896000 rw-p 00000000 00:00 0 
7fffed79c000-7fffed7bd000 rw-p 00000000 00:00 0                          [stack]
7fffed7e9000-7fffed7eb000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

所有這些都表明我的編譯錯誤還是我沒有正確使用它?


通過貢獻的更新檔,我們有:

echo -e '\0342\0230\0240' | /home/me/bin/trsc -dc '\342\230\240'
echo -e '☠' | /home/me/bin/trsc -dc '☠'
☠

正如我們應該!但:

/home/me/bin/trsc -dc '\342\230\240' < /dev/urandom

仍然是一個謎,因為選擇的角色不在輸出中……

我以前見過。一個錯誤。嘗試:

--- tr.c        6 Sep 2005 23:04:11 -0000       1.10
+++ tr.c        30 May 2014 09:46:33 -0000
@@ -291,7 +291,6 @@
               if(c<ccnt) code[c] = d;
               if(d<ccnt && sflag) squeez[d] = 1;
       }
-       free(vect);
       while((d = next(&string2)) != NIL) {
               if(sflag) squeez[d] = 1;
               if(string2.max==NIL && (string2.p==NULL || *string2.p==0))

(這是幾個月前的快速瀏覽,雖然這個更新檔會讓你繼續前進,但我不能保證它是正確的。使用 申請patch -l)。

現在還要注意/dev/urandom提供了一個字節流。在 UTF-8 中,並非所有字節序列都映射到有效字元。例如,0x41 0x81 0x41 是無效的,因為0x81>=0x80,所以它只能出現在超過 0x80 字節的 2 個或更多的序列中。

無效字節,因為它不在作為 ☠ 補碼的字元集中,不會被 刪除tr

更好的可能是:

recode ucs-2..u8 < /dev/urandom | tr -cd ☠

ucs-2 是 U+0000 到 U+FFFF 的字元,每個字元編碼 2 個字節,/dev/urandom看起來更像是 ucs-2 字元流。(雖然我們缺少 U+10000 到 U+10FFFF 的字元)。

但這仍然包括D800..DFFF 代理對范圍 ,它mbrtowc(3)會阻塞(至少在我的 libc 版本中)。

這些程式碼點保留用於 UTF-16 編碼。例如 d800dc00 是 U+10000 的 UTF-16BE 編碼,但沒有 U+D800 字元或 U+DC00。那些的 UTF-8 編碼作為一個字元也沒有意義(即使是相鄰的)。

所以你需要先排除它們:

perl -ne 'BEGIN{$/=\2;binmode STDOUT,":utf8"}
         $c = unpack("n",$_); if ($c < 0xd800 || $c > 0xdfff) {
           no warnings "utf8"; print chr($c)
         }' < /dev/urandom | tr -cd ☠

如果關鍵是要獲得以 UTF-8 編碼的隨機 Unicode 字元流,最好可能會獲得允許範圍內的隨機程式碼點(0..0xd7ff,0xf000..0x10ffff)並將其轉換為 UTF-8。如果你想基於它/dev/urandom,你可以為每個程式碼點使用 3 個字節(24 位):

perl -ne 'BEGIN{$/=\3;binmode STDOUT,":utf8"}
         $c = unpack("N","\0$_") * 0x10F800 >> 24;
         $c+=0x800 if $c >= 0xd800;
         do {no warnings "utf8"; print chr($c)}' < /dev/urandom |
 tr -cd ☠

引用自:https://unix.stackexchange.com/questions/132677