1818// Capturing arguments of CMP instructions, memcmp, and similar.
1919// WARNING: this code needs to have minimal dependencies.
2020
21+ #include < sys/time.h>
22+
2123#include < cstddef>
2224#include < cstdint>
2325#include < cstring>
2426
27+ #include " absl/base/optimization.h"
28+
2529namespace fuzztest ::internal {
2630
2731// Captures up to `kNumItems` different CMP argument pairs.
@@ -45,16 +49,38 @@ class CmpTrace {
4549 // No CTOR - objects will be created in TLS.
4650
4751 // Clears `this`.
48- void Clear () { memset ( this , 0 , sizeof (* this )) ; }
52+ void Clear () { to_clear = true ; }
4953
5054 // Captures one CMP argument pair, as two byte arrays, `size` bytes each.
5155 void Capture (uint8_t size, const uint8_t *value0, const uint8_t *value1) {
56+ if (ABSL_PREDICT_FALSE (to_clear)) {
57+ for (size_t i = 0 ; i < kNumItems ; ++i) {
58+ if (sizes_[i] == 0 ) break ;
59+ sizes_[i] = 0 ;
60+ }
61+ capture_count_ = 0 ;
62+ to_clear = false ;
63+ }
5264 if (size > kNumBytesPerValue ) size = kNumBytesPerValue ;
5365 // We choose a pseudo-random slot each time.
5466 // This way after capturing many pairs we end up with up to `kNumItems`
5567 // pairs which are typically, but not always, the most recent.
56- rand_seed_ = rand_seed_ * 1103515245 + 12345 ;
57- const size_t index = rand_seed_ % kNumItems ;
68+ size_t index = 0 ;
69+ if (capture_count_ < kNumItems ) {
70+ index = capture_count_++;
71+ } else {
72+ if (rand_seed_ == 0 ) {
73+ // Initialize the random seed (likely) once.
74+ struct timeval tv = {};
75+ constexpr size_t kUsecInSec = 1000000 ;
76+ gettimeofday (&tv, nullptr );
77+ rand_seed_ = tv.tv_sec * kUsecInSec + tv.tv_usec ;
78+ }
79+ capture_count_++;
80+ rand_seed_ = rand_seed_ * 1103515245 + 12345 ;
81+ index = rand_seed_ % capture_count_;
82+ if (index >= kNumItems ) return ;
83+ }
5884 Item& item = items_[index];
5985 sizes_[index] = size;
6086 __builtin_memcpy (item.value0 , value0, size);
@@ -74,12 +100,14 @@ class CmpTrace {
74100 // Iterates non-zero CMP pairs.
75101 template <typename Callback>
76102 void ForEachNonZero (Callback callback) {
103+ if (ABSL_PREDICT_FALSE (to_clear)) return ;
77104 for (size_t i = 0 ; i < kNumItems ; ++i) {
78105 const auto size = sizes_[i];
79- if (size == 0 || size > kNumBytesPerValue ) continue ;
80- sizes_[i] = 0 ;
106+ if (size == 0 ) break ;
107+ if (size > kNumBytesPerValue ) continue ;
81108 callback (size, items_[i].value0 , items_[i].value1 );
82109 }
110+ to_clear = true ;
83111 }
84112
85113 private:
@@ -89,17 +117,22 @@ class CmpTrace {
89117 uint8_t value1[kNumBytesPerValue ];
90118 };
91119
120+ volatile bool to_clear;
121+
92122 // Value sizes of argument pairs. zero-size indicates that the corresponding
93123 // entry is empty.
94124 //
95125 // Marked volatile because of the potential racing between the owning thread
96126 // and the main thread, which is tolerated gracefully.
97127 volatile uint8_t sizes_[kNumItems ];
128+
129+ size_t capture_count_;
98130 // Values of argument pairs.
99131 Item items_[kNumItems ];
100132
101- // Pseudo-random seed.
102- size_t rand_seed_;
133+ // Pseudo-random seed from glibc
134+ // (https://en.wikipedia.org/wiki/Linear_congruential_generator).
135+ uint32_t rand_seed_;
103136};
104137
105138} // namespace fuzztest::internal
0 commit comments