如果允許高性能算法中發(fā)現(xiàn)的許多方法在輸入緩沖區(qū)的末尾讀取少量內(nèi)容,則可以簡化這些方法。在此,“少量”通常表示W(wǎng) - 1末尾最多字節(jié),其中W的字長是算法字節(jié)數(shù)(例如,對于以64位塊為單位的輸入處理算法,最多為7個字節(jié))。顯然,一般來說,從輸入緩沖區(qū)的末尾寫入數(shù)據(jù)永遠(yuǎn)都不安全,因為您可能會破壞緩沖區(qū)1之外的數(shù)據(jù)。還很清楚,由于緩沖區(qū)的末尾可能無法讀取,因此將超出緩沖區(qū)末尾的內(nèi)容讀入另一頁可能會觸發(fā)分段錯誤/訪問沖突。但是,在讀取對齊值的特殊情況下,至少在x86上似乎無法出現(xiàn)頁面錯誤。在該平臺上,頁面(以及因此的內(nèi)存保護(hù)標(biāo)志)具有4K的粒度(可以使用更大的頁面,例如2MiB或1GiB,但它們是4K的倍數(shù)),因此對齊的讀取操作將僅訪問同一頁面中的字節(jié)作為有效字節(jié)緩沖區(qū)的一部分。這是一些循環(huán)的規(guī)范示例,該循環(huán)對齊其輸入并從緩沖區(qū)末尾讀取最多7個字節(jié):int processBytes(uint8_t *input, size_t size) { uint64_t *input64 = (uint64_t *)input, end64 = (uint64_t *)(input + size); int res; if (size < 8) { // special case for short inputs that we aren't concerned with here return shortMethod(); } // check the first 8 bytes if ((res = match(*input)) >= 0) { return input + res; } // align pointer to the next 8-byte boundary input64 = (ptrdiff_t)(input64 + 1) & ~0x7; for (; input64 < end64; input64++) { if ((res = match(*input64)) > 0) { return input + res < input + size ? input + res : -1; } } return -1;}內(nèi)部函數(shù)int match(uint64_t bytes)未顯示,但是它是尋找與特定模式匹配的字節(jié)的東西,如果找到則返回最低位置(0-7),否則返回-1。首先,為了簡化說明,將大小小于8的案例當(dāng)成了另一個函數(shù)。然后對前8個(未對齊的字節(jié))進(jìn)行一次檢查。然后,對剩余floor((size - 7) / 8)的8個字節(jié)2的塊進(jìn)行循環(huán)。該循環(huán)最多可以讀取緩沖區(qū)末尾的7個字節(jié)(當(dāng)時為7字節(jié)input & 0xF == 1)。但是,返回調(diào)用有一個檢查,該檢查排除了超出緩沖區(qū)末尾的任何虛假匹配。實際上,這樣的功能在x86和x86-64上安全嗎?這些類型的過度讀取在高性能代碼中很常見。避免這種過度讀取的特殊尾碼也很常見。有時,您會看到后者取代了前者,從而使valgrind等工具靜音。有時您會看到進(jìn)行此類替換的建議,但由于成語安全且工具錯誤(或過于保守)而被拒絕3。
請問在x86和x64的同一頁面中讀取緩沖區(qū)的末尾是否安全?
呼啦一陣風(fēng)
2019-11-04 10:48:31