libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

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_