/src/libjxl/lib/jxl/base/sanitizers.h
Line | Count | Source (jump to first uncovered line) |
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 | | using PixelSegment = std::pair<size_t, size_t>; |
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 | 10.7M | size_t size) {} Unexecuted instantiation: encode.cc:jxl::msan::PoisonMemory(void const*, unsigned long) Unexecuted instantiation: enc_jpeg_data.cc:jxl::msan::PoisonMemory(void const*, unsigned long) Unexecuted instantiation: dec_external_image.cc:jxl::msan::PoisonMemory(void const*, unsigned long) Unexecuted instantiation: dec_xyb.cc:jxl::msan::PoisonMemory(void const*, unsigned long) Unexecuted instantiation: render_pipeline.cc:jxl::msan::PoisonMemory(void const*, unsigned long) Unexecuted instantiation: simple_render_pipeline.cc:jxl::msan::PoisonMemory(void const*, unsigned long) stage_from_linear.cc:jxl::msan::PoisonMemory(void const*, unsigned long) Line | Count | Source | 216 | 3.28M | size_t size) {} |
stage_noise.cc:jxl::msan::PoisonMemory(void const*, unsigned long) Line | Count | Source | 216 | 463k | size_t size) {} |
Unexecuted instantiation: stage_to_linear.cc:jxl::msan::PoisonMemory(void const*, unsigned long) Unexecuted instantiation: stage_tone_mapping.cc:jxl::msan::PoisonMemory(void const*, unsigned long) stage_upsampling.cc:jxl::msan::PoisonMemory(void const*, unsigned long) Line | Count | Source | 216 | 3.46M | size_t size) {} |
stage_write.cc:jxl::msan::PoisonMemory(void const*, unsigned long) Line | Count | Source | 216 | 197k | size_t size) {} |
stage_xyb.cc:jxl::msan::PoisonMemory(void const*, unsigned long) Line | Count | Source | 216 | 3.28M | size_t size) {} |
Unexecuted instantiation: box_content_decoder.cc:jxl::msan::PoisonMemory(void const*, unsigned long) Unexecuted instantiation: dec_jpeg_data.cc:jxl::msan::PoisonMemory(void const*, unsigned long) |
217 | | static JXL_INLINE JXL_MAYBE_UNUSED void UnpoisonMemory(const void* m, |
218 | 12.9M | size_t size) {} Unexecuted instantiation: encode.cc:jxl::msan::UnpoisonMemory(void const*, unsigned long) Unexecuted instantiation: enc_jpeg_data.cc:jxl::msan::UnpoisonMemory(void const*, unsigned long) Unexecuted instantiation: dec_external_image.cc:jxl::msan::UnpoisonMemory(void const*, unsigned long) Unexecuted instantiation: dec_xyb.cc:jxl::msan::UnpoisonMemory(void const*, unsigned long) Unexecuted instantiation: render_pipeline.cc:jxl::msan::UnpoisonMemory(void const*, unsigned long) Unexecuted instantiation: simple_render_pipeline.cc:jxl::msan::UnpoisonMemory(void const*, unsigned long) stage_from_linear.cc:jxl::msan::UnpoisonMemory(void const*, unsigned long) Line | Count | Source | 218 | 3.28M | size_t size) {} |
stage_noise.cc:jxl::msan::UnpoisonMemory(void const*, unsigned long) Line | Count | Source | 218 | 309k | size_t size) {} |
Unexecuted instantiation: stage_to_linear.cc:jxl::msan::UnpoisonMemory(void const*, unsigned long) Unexecuted instantiation: stage_tone_mapping.cc:jxl::msan::UnpoisonMemory(void const*, unsigned long) stage_upsampling.cc:jxl::msan::UnpoisonMemory(void const*, unsigned long) Line | Count | Source | 218 | 5.39M | size_t size) {} |
stage_write.cc:jxl::msan::UnpoisonMemory(void const*, unsigned long) Line | Count | Source | 218 | 627k | size_t size) {} |
stage_xyb.cc:jxl::msan::UnpoisonMemory(void const*, unsigned long) Line | Count | Source | 218 | 3.28M | size_t size) {} |
Unexecuted instantiation: box_content_decoder.cc:jxl::msan::UnpoisonMemory(void const*, unsigned long) Unexecuted instantiation: dec_jpeg_data.cc:jxl::msan::UnpoisonMemory(void const*, unsigned long) |
219 | | static JXL_INLINE JXL_MAYBE_UNUSED void MemoryIsInitialized(const void* m, |
220 | 0 | size_t size) {} Unexecuted instantiation: encode.cc:jxl::msan::MemoryIsInitialized(void const*, unsigned long) Unexecuted instantiation: enc_jpeg_data.cc:jxl::msan::MemoryIsInitialized(void const*, unsigned long) Unexecuted instantiation: dec_external_image.cc:jxl::msan::MemoryIsInitialized(void const*, unsigned long) Unexecuted instantiation: dec_xyb.cc:jxl::msan::MemoryIsInitialized(void const*, unsigned long) Unexecuted instantiation: render_pipeline.cc:jxl::msan::MemoryIsInitialized(void const*, unsigned long) Unexecuted instantiation: simple_render_pipeline.cc:jxl::msan::MemoryIsInitialized(void const*, unsigned long) Unexecuted instantiation: stage_from_linear.cc:jxl::msan::MemoryIsInitialized(void const*, unsigned long) Unexecuted instantiation: stage_noise.cc:jxl::msan::MemoryIsInitialized(void const*, unsigned long) Unexecuted instantiation: stage_to_linear.cc:jxl::msan::MemoryIsInitialized(void const*, unsigned long) Unexecuted instantiation: stage_tone_mapping.cc:jxl::msan::MemoryIsInitialized(void const*, unsigned long) Unexecuted instantiation: stage_upsampling.cc:jxl::msan::MemoryIsInitialized(void const*, unsigned long) Unexecuted instantiation: stage_write.cc:jxl::msan::MemoryIsInitialized(void const*, unsigned long) Unexecuted instantiation: stage_xyb.cc:jxl::msan::MemoryIsInitialized(void const*, unsigned long) Unexecuted instantiation: box_content_decoder.cc:jxl::msan::MemoryIsInitialized(void const*, unsigned long) Unexecuted instantiation: dec_jpeg_data.cc:jxl::msan::MemoryIsInitialized(void const*, unsigned long) |
221 | | |
222 | | template <typename Pixels> |
223 | 0 | 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_ |