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