sanitizers.h (7863B)
1 // Copyright (c) the JPEG XL Project Authors. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 #ifndef LIB_JXL_SANITIZERS_H_ 7 #define LIB_JXL_SANITIZERS_H_ 8 9 #include <cstddef> 10 11 #include "lib/jxl/base/compiler_specific.h" 12 #include "lib/jxl/base/sanitizer_definitions.h" 13 14 #if JXL_MEMORY_SANITIZER 15 #include <algorithm> 16 #include <cinttypes> // PRId64 17 #include <cstdio> 18 #include <string> 19 #include <vector> 20 21 #include "lib/jxl/base/rect.h" 22 #include "lib/jxl/base/status.h" 23 #include "sanitizer/msan_interface.h" 24 #endif 25 26 namespace jxl { 27 namespace msan { 28 29 #if JXL_MEMORY_SANITIZER 30 31 // Chosen so that kSanitizerSentinel is four copies of kSanitizerSentinelByte. 32 constexpr uint8_t kSanitizerSentinelByte = 0x48; 33 constexpr float kSanitizerSentinel = 205089.125f; 34 35 static JXL_INLINE JXL_MAYBE_UNUSED void PoisonMemory(const volatile void* m, 36 size_t size) { 37 __msan_poison(m, size); 38 } 39 40 static JXL_INLINE JXL_MAYBE_UNUSED void UnpoisonMemory(const volatile void* m, 41 size_t size) { 42 __msan_unpoison(m, size); 43 } 44 45 static JXL_INLINE JXL_MAYBE_UNUSED void MemoryIsInitialized( 46 const volatile void* m, size_t size) { 47 __msan_check_mem_is_initialized(m, size); 48 } 49 50 // Mark all the bytes of an image (including padding) as poisoned bytes. 51 template <typename Pixels> 52 static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const Pixels& im) { 53 PoisonMemory(im.bytes(), im.bytes_per_row() * im.ysize()); 54 } 55 56 namespace { 57 58 // Print the uninitialized regions of an image. 59 template <typename Pixels> 60 static JXL_INLINE JXL_MAYBE_UNUSED void PrintImageUninitialized( 61 const Pixels& im) { 62 fprintf(stderr, 63 "Uninitialized regions for image of size %" PRIu64 "x%" PRIu64 ":\n", 64 static_cast<uint64_t>(im.xsize()), static_cast<uint64_t>(im.ysize())); 65 66 // A segment of uninitialized pixels in a row, in the format [first, second). 67 typedef std::pair<size_t, size_t> PixelSegment; 68 69 // Helper class to merge and print a list of rows of PixelSegment that may be 70 // the same over big ranges of rows. This compacts the output to ranges of 71 // rows like "[y0, y1): [x0, x1) [x2, x3)". 72 class RowsMerger { 73 public: 74 // Add a new row the list of rows. If the row is the same as the previous 75 // one it will be merged showing a range of rows [y0, y1), but if the new 76 // row is different the current range of rows (if any) will be printed and a 77 // new one will be started. 78 void AddRow(size_t y, std::vector<PixelSegment>&& new_row) { 79 if (start_y_ != -1 && new_row != segments_) { 80 PrintRow(y); 81 } 82 if (new_row.empty()) { 83 // Skip ranges with no uninitialized pixels. 84 start_y_ = -1; 85 segments_.clear(); 86 return; 87 } 88 if (start_y_ == -1) { 89 start_y_ = y; 90 segments_ = std::move(new_row); 91 } 92 } 93 94 // Print the contents of the range of rows [start_y_, end_y) if any. 95 void PrintRow(size_t end_y) { 96 if (start_y_ == -1) return; 97 if (segments_.empty()) { 98 start_y_ = -1; 99 return; 100 } 101 if (end_y - start_y_ > 1) { 102 fprintf(stderr, " y=[%" PRId64 ", %" PRIu64 "):", 103 static_cast<int64_t>(start_y_), static_cast<uint64_t>(end_y)); 104 } else { 105 fprintf(stderr, " y=[%" PRId64 "]:", static_cast<int64_t>(start_y_)); 106 } 107 for (const auto& seg : segments_) { 108 if (seg.first + 1 == seg.second) { 109 fprintf(stderr, " [%" PRId64 "]", static_cast<int64_t>(seg.first)); 110 } else { 111 fprintf(stderr, " [%" PRId64 ", %" PRIu64 ")", 112 static_cast<int64_t>(seg.first), 113 static_cast<uint64_t>(seg.second)); 114 } 115 } 116 fprintf(stderr, "\n"); 117 start_y_ = -1; 118 } 119 120 private: 121 std::vector<PixelSegment> segments_; 122 // Row number of the first row in the range of rows that have |segments| as 123 // the undefined segments. 124 ssize_t start_y_ = -1; 125 } rows_merger; 126 127 class SegmentsMerger { 128 public: 129 void AddValue(size_t x) { 130 if (row.empty() || row.back().second != x) { 131 row.emplace_back(x, x + 1); 132 } else { 133 row.back().second = x + 1; 134 } 135 } 136 137 std::vector<PixelSegment> row; 138 }; 139 140 for (size_t y = 0; y < im.ysize(); y++) { 141 auto* row = im.Row(y); 142 SegmentsMerger seg_merger; 143 size_t x = 0; 144 while (x < im.xsize()) { 145 intptr_t ret = 146 __msan_test_shadow(row + x, (im.xsize() - x) * sizeof(row[0])); 147 if (ret < 0) break; 148 size_t next_x = x + ret / sizeof(row[0]); 149 seg_merger.AddValue(next_x); 150 x = next_x + 1; 151 } 152 rows_merger.AddRow(y, std::move(seg_merger.row)); 153 } 154 rows_merger.PrintRow(im.ysize()); 155 } 156 157 // Check that all the pixels in the provided rect of the image are initialized 158 // (not poisoned). If any of the values is poisoned it will abort. 159 template <typename Pixels> 160 static JXL_INLINE JXL_MAYBE_UNUSED void CheckImageInitialized( 161 const Pixels& im, const Rect& r, size_t c, const char* message) { 162 JXL_DASSERT(r.x0() <= im.xsize()); 163 JXL_DASSERT(r.x0() + r.xsize() <= im.xsize()); 164 JXL_DASSERT(r.y0() <= im.ysize()); 165 JXL_DASSERT(r.y0() + r.ysize() <= im.ysize()); 166 for (size_t y = r.y0(); y < r.y0() + r.ysize(); y++) { 167 const auto* row = im.Row(y); 168 intptr_t ret = __msan_test_shadow(row + r.x0(), sizeof(*row) * r.xsize()); 169 if (ret != -1) { 170 JXL_DEBUG( 171 1, 172 "Checking an image of %" PRIu64 " x %" PRIu64 ", rect x0=%" PRIu64 173 ", y0=%" PRIu64 174 ", " 175 "xsize=%" PRIu64 ", ysize=%" PRIu64, 176 static_cast<uint64_t>(im.xsize()), static_cast<uint64_t>(im.ysize()), 177 static_cast<uint64_t>(r.x0()), static_cast<uint64_t>(r.y0()), 178 static_cast<uint64_t>(r.xsize()), static_cast<uint64_t>(r.ysize())); 179 size_t x = ret / sizeof(*row); 180 JXL_DEBUG(1, 181 "CheckImageInitialized failed at x=%" PRIu64 ", y=%" PRIu64 182 ", c=%" PRIu64 ": %s", 183 static_cast<uint64_t>(r.x0() + x), static_cast<uint64_t>(y), 184 static_cast<uint64_t>(c), message ? message : ""); 185 PrintImageUninitialized(im); 186 } 187 // This will report an error if memory is not initialized. 188 __msan_check_mem_is_initialized(row + r.x0(), sizeof(*row) * r.xsize()); 189 } 190 } 191 192 template <typename Image> 193 static JXL_INLINE JXL_MAYBE_UNUSED void CheckImageInitialized( 194 const Image& im, const Rect& r, const char* message) { 195 for (size_t c = 0; c < 3; c++) { 196 std::string str_message(message); 197 str_message += " c=" + std::to_string(c); 198 CheckImageInitialized(im.Plane(c), r, c, str_message.c_str()); 199 } 200 } 201 202 } // namespace 203 204 #define JXL_CHECK_IMAGE_INITIALIZED(im, r) \ 205 ::jxl::msan::CheckImageInitialized(im, r, "im=" #im ", r=" #r); 206 207 #define JXL_CHECK_PLANE_INITIALIZED(im, r, c) \ 208 ::jxl::msan::CheckImageInitialized(im, r, c, "im=" #im ", r=" #r ", c=" #c); 209 210 #else // JXL_MEMORY_SANITIZER 211 212 // In non-msan mode these functions don't use volatile since it is not needed 213 // for the empty functions. 214 215 static JXL_INLINE JXL_MAYBE_UNUSED void PoisonMemory(const void* m, 216 size_t size) {} 217 static JXL_INLINE JXL_MAYBE_UNUSED void UnpoisonMemory(const void* m, 218 size_t size) {} 219 static JXL_INLINE JXL_MAYBE_UNUSED void MemoryIsInitialized(const void* m, 220 size_t size) {} 221 222 template <typename Pixels> 223 static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const Pixels& im) {} 224 225 #define JXL_CHECK_IMAGE_INITIALIZED(im, r) 226 #define JXL_CHECK_PLANE_INITIALIZED(im, r, c) 227 228 #endif 229 230 } // namespace msan 231 } // namespace jxl 232 233 #endif // LIB_JXL_SANITIZERS_H_