Coverage Report

Created: 2025-06-16 07:00

/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_