Skip to main content

foundry_evm_fuzz/strategies/
mutators.rs

1use alloy_dyn_abi::Word;
2use alloy_primitives::{Address, I256, Sign, U256};
3use proptest::{prelude::*, test_runner::TestRunner};
4use rand::seq::IndexedRandom;
5use std::fmt::Debug;
6
7// Interesting 8-bit values to inject.
8static INTERESTING_8: &[i8] = &[-128, -1, 0, 1, 16, 32, 64, 100, 127];
9
10/// Interesting 16-bit values to inject.
11static INTERESTING_16: &[i16] = &[
12    -128, -1, 0, 1, 16, 32, 64, 100, 127, -32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767,
13];
14
15/// Interesting 32-bit values to inject.
16static INTERESTING_32: &[i32] = &[
17    -128,
18    -1,
19    0,
20    1,
21    16,
22    32,
23    64,
24    100,
25    127,
26    -32768,
27    -129,
28    128,
29    255,
30    256,
31    512,
32    1000,
33    1024,
34    4096,
35    32767,
36    -2147483648,
37    -100663046,
38    -32769,
39    32768,
40    65535,
41    65536,
42    100663045,
43    2147483647,
44];
45
46/// Multipliers used to define the 3 standard deviation range of a Gaussian-like curve.
47/// For example, a multiplier of 0.25 means the +/-3 standard deviation bounds are +/-25% of the
48/// original value.
49static THREE_SIGMA_MULTIPLIERS: &[f64] = &[0.1, 0.25, 0.5, 1.0, 2.0, 5.0, 10.0];
50
51/// Mutator that randomly increments or decrements an uint or int.
52pub(crate) trait IncrementDecrementMutator: Sized + Copy + Debug {
53    fn validate(old: Self, new: Self, size: usize) -> Option<Self>;
54
55    #[instrument(
56        name = "mutator::increment_decrement",
57        level = "trace",
58        skip(size, test_runner),
59        ret
60    )]
61    fn increment_decrement(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
62        let mutated = if test_runner.rng().random::<bool>() {
63            self.wrapping_add(Self::ONE)
64        } else {
65            self.wrapping_sub(Self::ONE)
66        };
67        Self::validate(self, mutated, size)
68    }
69
70    fn wrapping_add(self, rhs: Self) -> Self;
71    fn wrapping_sub(self, rhs: Self) -> Self;
72    const ONE: Self;
73}
74
75macro_rules! impl_increment_decrement_mutator {
76    ($ty:ty, $validate_fn:path) => {
77        impl IncrementDecrementMutator for $ty {
78            fn validate(old: Self, new: Self, size: usize) -> Option<Self> {
79                $validate_fn(old, new, size)
80            }
81
82            fn wrapping_add(self, rhs: Self) -> Self {
83                Self::wrapping_add(self, rhs)
84            }
85
86            fn wrapping_sub(self, rhs: Self) -> Self {
87                Self::wrapping_sub(self, rhs)
88            }
89
90            const ONE: Self = Self::ONE;
91        }
92    };
93}
94
95impl_increment_decrement_mutator!(U256, validate_uint_mutation);
96impl_increment_decrement_mutator!(I256, validate_int_mutation);
97
98/// Mutator that changes the current value of an uint or int by applying gaussian noise.
99pub(crate) trait GaussianNoiseMutator: Sized + Copy + Debug {
100    fn mutate_with_gaussian_noise(self, size: usize, test_runner: &mut TestRunner) -> Option<Self>;
101}
102
103impl GaussianNoiseMutator for U256 {
104    #[instrument(
105        name = "U256::mutate_with_gaussian_noise",
106        level = "trace",
107        skip(size, test_runner),
108        ret
109    )]
110    fn mutate_with_gaussian_noise(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
111        let scale_factor = sample_gaussian_scale(&mut test_runner.rng())?;
112        let mut bytes: [u8; 32] = self.to_be_bytes();
113        apply_scale_to_bytes(&mut bytes[32 - size / 8..], scale_factor)?;
114        validate_uint_mutation(self, Self::from_be_bytes(bytes), size)
115    }
116}
117
118impl GaussianNoiseMutator for I256 {
119    #[instrument(
120        name = "I256::mutate_with_gaussian_noise",
121        level = "trace",
122        skip(size, test_runner),
123        ret
124    )]
125    fn mutate_with_gaussian_noise(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
126        let scale_factor = sample_gaussian_scale(&mut test_runner.rng())?;
127        let mut bytes: [u8; 32] = self.to_be_bytes();
128        apply_scale_to_bytes(&mut bytes[32 - size / 8..], scale_factor)?;
129        validate_int_mutation(self, Self::from_be_bytes(bytes), size)
130    }
131}
132
133/// Mutator that bounds the current value of an uint or int in the given range.
134/// The mutated value is always different from the current value.
135pub trait BoundMutator: Sized + Copy + Debug {
136    fn bound(self, min: Self, max: Self, test_runner: &mut TestRunner) -> Option<Self>;
137}
138
139impl BoundMutator for U256 {
140    #[instrument(name = "U256::bound", level = "trace", skip(test_runner), ret)]
141    fn bound(self, min: Self, max: Self, test_runner: &mut TestRunner) -> Option<Self> {
142        if min > max || self < min || self > max || min == max {
143            return None;
144        }
145
146        let rng = test_runner.rng();
147
148        loop {
149            let bits = rng.random_range(8..=256);
150            let mask = (Self::ONE << bits) - Self::ONE;
151            let candidate = Self::from(rng.random::<u128>()) & mask;
152
153            // Map to range.
154            let candidate = min + (candidate % ((max - min).saturating_add(Self::ONE)));
155
156            if candidate != self {
157                return Some(candidate);
158            }
159        }
160    }
161}
162
163impl BoundMutator for I256 {
164    #[instrument(name = "I256::bound", level = "trace", skip(test_runner), ret)]
165    fn bound(self, min: Self, max: Self, test_runner: &mut TestRunner) -> Option<Self> {
166        if min > max || self < min || self > max || min == max {
167            return None;
168        }
169
170        let rng = test_runner.rng();
171
172        loop {
173            let bits = rng.random_range(8..=255);
174            let mask = (U256::ONE << bits) - U256::ONE;
175            let rand_u = U256::from(rng.next_u64()) | (U256::from(rng.next_u64()) << 64);
176            let unsigned_candidate = rand_u & mask;
177
178            let signed_candidate = {
179                let midpoint = U256::ONE << (bits - 1);
180                if unsigned_candidate < midpoint {
181                    Self::from_raw(unsigned_candidate)
182                } else {
183                    Self::from_raw(unsigned_candidate) - Self::from_raw(U256::ONE << bits)
184                }
185            };
186
187            // Map to range.
188            let range = max.saturating_sub(min).saturating_add(Self::ONE).unsigned_abs();
189            let wrapped = Self::from_raw(U256::from(signed_candidate.unsigned_abs()) % range);
190            let candidate =
191                if signed_candidate.is_negative() { max - wrapped } else { min + wrapped };
192
193            if candidate != self {
194                return Some(candidate);
195            }
196        }
197    }
198}
199
200/// Mutator that changes the current value by flipping a random bit.
201pub(crate) trait BitMutator: Sized + Copy + Debug {
202    fn flip_random_bit(self, size: usize, test_runner: &mut TestRunner) -> Option<Self>;
203}
204
205impl BitMutator for U256 {
206    #[instrument(name = "U256::flip_random_bit", level = "trace", skip(size, test_runner), ret)]
207    fn flip_random_bit(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
208        let mut bytes: [u8; 32] = self.to_be_bytes();
209        flip_random_bit_in_slice(&mut bytes[32 - size / 8..], test_runner)?;
210        validate_uint_mutation(self, Self::from_be_bytes(bytes), size)
211    }
212}
213
214impl BitMutator for I256 {
215    #[instrument(name = "I256::flip_random_bit", level = "trace", skip(size, test_runner), ret)]
216    fn flip_random_bit(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
217        let mut bytes: [u8; 32] = self.to_be_bytes();
218        flip_random_bit_in_slice(&mut bytes[32 - size / 8..], test_runner)?;
219        validate_int_mutation(self, Self::from_be_bytes(bytes), size)
220    }
221}
222
223impl BitMutator for Address {
224    #[instrument(name = "Address::flip_random_bit", level = "trace", skip(_size, test_runner), ret)]
225    fn flip_random_bit(self, _size: usize, test_runner: &mut TestRunner) -> Option<Self> {
226        let mut mutated = self;
227        flip_random_bit_in_slice(mutated.as_mut_slice(), test_runner)?;
228        (self != mutated).then_some(mutated)
229    }
230}
231
232impl BitMutator for Word {
233    #[instrument(name = "Word::flip_random_bit", level = "trace", skip(size, test_runner), ret)]
234    fn flip_random_bit(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
235        let mut bytes = self;
236        let slice = &mut bytes[..size];
237        flip_random_bit_in_slice(slice, test_runner)?;
238        (self != bytes).then_some(bytes)
239    }
240}
241
242/// Mutator that changes the current value by randomly injecting interesting words (for uint, int,
243/// address and fixed bytes) - see <https://github.com/AFLplusplus/LibAFL/blob/90cb9a2919faf386e0678870e52784070cdac4b6/crates/libafl/src/mutators/mutations.rs#L88-L123>.
244pub(crate) trait InterestingWordMutator: Sized + Copy + Debug {
245    fn mutate_interesting_byte(self, size: usize, test_runner: &mut TestRunner) -> Option<Self>;
246    fn mutate_interesting_word(self, size: usize, test_runner: &mut TestRunner) -> Option<Self>;
247    fn mutate_interesting_dword(self, size: usize, test_runner: &mut TestRunner) -> Option<Self>;
248}
249
250impl InterestingWordMutator for U256 {
251    #[instrument(
252        name = "U256::mutate_interesting_byte",
253        level = "trace",
254        skip(size, test_runner),
255        ret
256    )]
257    fn mutate_interesting_byte(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
258        let mut bytes: [u8; 32] = self.to_be_bytes();
259        mutate_interesting_byte_slice(&mut bytes[32 - size / 8..], test_runner)?;
260        validate_uint_mutation(self, Self::from_be_bytes(bytes), size)
261    }
262
263    #[instrument(
264        name = "U256::mutate_interesting_word",
265        level = "trace",
266        skip(size, test_runner),
267        ret
268    )]
269    fn mutate_interesting_word(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
270        let mut bytes: [u8; 32] = self.to_be_bytes();
271        mutate_interesting_word_slice(&mut bytes[32 - size / 8..], test_runner)?;
272        validate_uint_mutation(self, Self::from_be_bytes(bytes), size)
273    }
274
275    #[instrument(
276        name = "U256::mutate_interesting_dword",
277        level = "trace",
278        skip(size, test_runner),
279        ret
280    )]
281    fn mutate_interesting_dword(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
282        let mut bytes: [u8; 32] = self.to_be_bytes();
283        mutate_interesting_dword_slice(&mut bytes[32 - size / 8..], test_runner)?;
284        validate_uint_mutation(self, Self::from_be_bytes(bytes), size)
285    }
286}
287
288impl InterestingWordMutator for I256 {
289    #[instrument(
290        name = "I256::mutate_interesting_byte",
291        level = "trace",
292        skip(size, test_runner),
293        ret
294    )]
295    fn mutate_interesting_byte(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
296        let mut bytes: [u8; 32] = self.to_be_bytes();
297        mutate_interesting_byte_slice(&mut bytes[32 - size / 8..], test_runner)?;
298        validate_int_mutation(self, Self::from_be_bytes(bytes), size)
299    }
300
301    #[instrument(
302        name = "I256::mutate_interesting_word",
303        level = "trace",
304        skip(size, test_runner),
305        ret
306    )]
307    fn mutate_interesting_word(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
308        let mut bytes: [u8; 32] = self.to_be_bytes();
309        mutate_interesting_word_slice(&mut bytes[32 - size / 8..], test_runner)?;
310        validate_int_mutation(self, Self::from_be_bytes(bytes), size)
311    }
312
313    #[instrument(
314        name = "I256::mutate_interesting_dword",
315        level = "trace",
316        skip(size, test_runner),
317        ret
318    )]
319    fn mutate_interesting_dword(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
320        let mut bytes: [u8; 32] = self.to_be_bytes();
321        mutate_interesting_dword_slice(&mut bytes[32 - size / 8..], test_runner)?;
322        validate_int_mutation(self, Self::from_be_bytes(bytes), size)
323    }
324}
325
326impl InterestingWordMutator for Address {
327    #[instrument(
328        name = "Address::mutate_interesting_byte",
329        level = "trace",
330        skip(_size, test_runner),
331        ret
332    )]
333    fn mutate_interesting_byte(self, _size: usize, test_runner: &mut TestRunner) -> Option<Self> {
334        let mut mutated = self;
335        mutate_interesting_byte_slice(mutated.as_mut_slice(), test_runner)?;
336        (self != mutated).then_some(mutated)
337    }
338
339    #[instrument(
340        name = "Address::mutate_interesting_word",
341        level = "trace",
342        skip(_size, test_runner),
343        ret
344    )]
345    fn mutate_interesting_word(self, _size: usize, test_runner: &mut TestRunner) -> Option<Self> {
346        let mut mutated = self;
347        mutate_interesting_word_slice(mutated.as_mut_slice(), test_runner)?;
348        (self != mutated).then_some(mutated)
349    }
350
351    #[instrument(
352        name = "Address::mutate_interesting_dword",
353        level = "trace",
354        skip(_size, test_runner),
355        ret
356    )]
357    fn mutate_interesting_dword(self, _size: usize, test_runner: &mut TestRunner) -> Option<Self> {
358        let mut mutated = self;
359        mutate_interesting_dword_slice(mutated.as_mut_slice(), test_runner)?;
360        (self != mutated).then_some(mutated)
361    }
362}
363
364impl InterestingWordMutator for Word {
365    #[instrument(
366        name = "Word::mutate_interesting_byte",
367        level = "trace",
368        skip(size, test_runner),
369        ret
370    )]
371    fn mutate_interesting_byte(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
372        let mut bytes = self;
373        let slice = &mut bytes[..size];
374        mutate_interesting_byte_slice(slice, test_runner)?;
375        (self != bytes).then_some(bytes)
376    }
377
378    #[instrument(
379        name = "Word::mutate_interesting_word",
380        level = "trace",
381        skip(size, test_runner),
382        ret
383    )]
384    fn mutate_interesting_word(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
385        let mut bytes = self;
386        let slice = &mut bytes[..size];
387        mutate_interesting_word_slice(slice, test_runner)?;
388        (self != bytes).then_some(bytes)
389    }
390
391    #[instrument(
392        name = "Word::mutate_interesting_dword",
393        level = "trace",
394        skip(size, test_runner),
395        ret
396    )]
397    fn mutate_interesting_dword(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
398        let mut bytes = self;
399        let slice = &mut bytes[..size];
400        mutate_interesting_dword_slice(slice, test_runner)?;
401        (self != bytes).then_some(bytes)
402    }
403}
404
405/// Flips a random bit in the given mutable byte slice.
406fn flip_random_bit_in_slice(bytes: &mut [u8], test_runner: &mut TestRunner) -> Option<()> {
407    if bytes.is_empty() {
408        return None;
409    }
410    let bit_index = test_runner.rng().random_range(0..(bytes.len() * 8));
411    bytes[bit_index / 8] ^= 1 << (bit_index % 8);
412    Some(())
413}
414
415/// Mutates a random byte in the given byte slice by replacing it with a randomly chosen
416/// interesting 8-bit value.
417fn mutate_interesting_byte_slice(bytes: &mut [u8], test_runner: &mut TestRunner) -> Option<()> {
418    let index = test_runner.rng().random_range(0..bytes.len());
419    let val = *INTERESTING_8.choose(&mut test_runner.rng())? as u8;
420    bytes[index] = val;
421    Some(())
422}
423
424/// Mutates a random 2-byte (16-bit) region in the byte slice with a randomly chosen interesting
425/// 16-bit value.
426fn mutate_interesting_word_slice(bytes: &mut [u8], test_runner: &mut TestRunner) -> Option<()> {
427    if bytes.len() < 2 {
428        return None;
429    }
430    let index = test_runner.rng().random_range(0..=bytes.len() - 2);
431    let val = *INTERESTING_16.choose(&mut test_runner.rng())? as u16;
432    bytes[index..index + 2].copy_from_slice(&val.to_be_bytes());
433    Some(())
434}
435
436/// Mutates a random 4-byte (32-bit) region in the byte slice with a randomly chosen interesting
437/// 32-bit value.
438fn mutate_interesting_dword_slice(bytes: &mut [u8], test_runner: &mut TestRunner) -> Option<()> {
439    if bytes.len() < 4 {
440        return None;
441    }
442    let index = test_runner.rng().random_range(0..=bytes.len() - 4);
443    let val = *INTERESTING_32.choose(&mut test_runner.rng())? as u32;
444    bytes[index..index + 4].copy_from_slice(&val.to_be_bytes());
445    Some(())
446}
447
448/// Samples a scale factor from a pseudo-Gaussian distribution centered around 1.0.
449///
450/// - Select a random standard deviation multiplier from a predefined set.
451/// - Approximates a standard normal distribution using the Irwin-Hall method (sum of uniform
452///   samples).
453/// - Scales the normal value by the chosen standard deviation multiplier, divided by 3 to get
454///   standard deviation.
455/// - Adds 1.0 to center the scale factor around 1.0 (no mutation).
456///
457/// Returns a scale factor that, when applied to a number, mimics Gaussian noise.
458fn sample_gaussian_scale<R: Rng>(rng: &mut R) -> Option<f64> {
459    let num_samples = 8;
460    let chosen_3rd_sigma = *THREE_SIGMA_MULTIPLIERS.choose(rng).unwrap_or(&1.0);
461
462    let mut sum = 0.0;
463    for _ in 0..num_samples {
464        sum += rng.random::<f64>();
465    }
466
467    let standard_normal = sum - (num_samples as f64 / 2.0);
468    let mut scale_factor = (chosen_3rd_sigma / 3.0) * standard_normal;
469    scale_factor += 1.0;
470
471    if scale_factor < 0.0 || (scale_factor - 1.0).abs() < f64::EPSILON {
472        None
473    } else {
474        Some(scale_factor)
475    }
476}
477
478/// Applies a floating-point scale factor to a byte slice representing an unsigned or signed
479/// integer.
480fn apply_scale_to_bytes(bytes: &mut [u8], scale_factor: f64) -> Option<()> {
481    let mut carry_down = 0.0;
482
483    for i in (0..bytes.len()).rev() {
484        let byte_val = bytes[i] as f64;
485        let scaled = (byte_val + carry_down * 256.0) * scale_factor;
486
487        if i == 0 && scaled >= 256.0 {
488            for b in bytes.iter_mut() {
489                *b = 0xFF;
490            }
491            return Some(());
492        }
493
494        bytes[i] = (scaled % 256.0).floor() as u8;
495
496        let mut carry_up = (scaled / 256.0).floor();
497        carry_down = (scaled % 1.0) / scale_factor;
498
499        let mut j = i;
500        // Propagate carry_up until it is zero or no more bytes left
501        while carry_up > 0.0 && j > 0 {
502            j -= 1;
503            let new_val = bytes[j] as f64 + carry_up;
504            if j == 0 && new_val >= 256.0 {
505                for b in bytes.iter_mut() {
506                    *b = 0xFF;
507                }
508                return Some(());
509            }
510            bytes[j] = (new_val % 256.0).floor() as u8;
511            carry_up = (new_val / 256.0).floor();
512        }
513    }
514
515    Some(())
516}
517
518/// Returns mutated uint value if different from the original value and if it fits in the given
519/// size, otherwise None.
520fn validate_uint_mutation(original: U256, mutated: U256, size: usize) -> Option<U256> {
521    // Early return if mutated value is the same as original value.
522    if mutated == original {
523        return None;
524    }
525
526    // Check if mutated value fits the given size.
527    let max = if size < 256 { (U256::from(1) << size) - U256::from(1) } else { U256::MAX };
528    (mutated <= max).then_some(mutated)
529}
530
531/// Returns mutated int value if different from the original value and if it fits in the given size,
532/// otherwise None.
533fn validate_int_mutation(original: I256, mutated: I256, size: usize) -> Option<I256> {
534    // Early return if mutated value is the same as original value.
535    if mutated == original {
536        return None;
537    }
538
539    // Check if mutated value fits the given size.
540    let max_abs = (U256::from(1) << (size - 1)) - U256::from(1);
541    match mutated.sign() {
542        Sign::Positive => mutated < I256::overflowing_from_sign_and_abs(Sign::Positive, max_abs).0,
543        Sign::Negative => mutated > I256::overflowing_from_sign_and_abs(Sign::Negative, max_abs).0,
544    }
545    .then_some(mutated)
546}
547
548#[cfg(test)]
549mod tests {
550    use super::*;
551    use proptest::test_runner::Config;
552
553    #[test]
554    fn validate_uint_mutation_accepts_inclusive_max() {
555        // For width `size`, valid unsigned values are 0..=max where max = 2^size - 1.
556        // Regression: `< max` incorrectly rejected `mutated == max`; must use `<= max`.
557        for size in [8usize, 16, 32, 64, 128] {
558            let max = (U256::from(1) << size) - U256::from(1);
559            assert_eq!(
560                super::validate_uint_mutation(U256::ZERO, max, size),
561                Some(max),
562                "size={size}"
563            );
564        }
565        let original = U256::from(1);
566        assert_eq!(super::validate_uint_mutation(original, U256::MAX, 256), Some(U256::MAX));
567    }
568
569    #[test]
570    fn test_mutate_uint() {
571        let mut runner = TestRunner::new(Config::default());
572        let size = 32;
573
574        let test_values =
575            vec![U256::ZERO, U256::ONE, U256::from(12345u64), U256::from(255), U256::MAX];
576
577        #[track_caller]
578        fn validate_mutation(value: U256, mutated: Option<U256>) {
579            assert!(
580                mutated.is_none() || mutated.is_some_and(|m| m != value),
581                "Mutation failed: value = {value:?}, mutated = {mutated:?}"
582            );
583        }
584
585        for value in test_values {
586            for _ in 0..100 {
587                validate_mutation(value, U256::increment_decrement(value, size, &mut runner));
588                validate_mutation(value, U256::flip_random_bit(value, size, &mut runner));
589                validate_mutation(value, U256::mutate_interesting_byte(value, size, &mut runner));
590                validate_mutation(value, U256::mutate_interesting_word(value, size, &mut runner));
591                validate_mutation(value, U256::mutate_interesting_dword(value, size, &mut runner));
592            }
593        }
594    }
595
596    #[test]
597    fn test_mutate_int() {
598        let mut runner = TestRunner::new(Config::default());
599        let size = 32;
600
601        let test_values = vec![
602            I256::ZERO,
603            I256::ONE,
604            I256::MINUS_ONE,
605            I256::from_dec_str("12345").unwrap(),
606            I256::from_dec_str("-54321").unwrap(),
607            I256::from_dec_str("340282366920938463463374607431768211455").unwrap(),
608            I256::from_dec_str("-340282366920938463463374607431768211455").unwrap(),
609        ];
610
611        #[track_caller]
612        fn validate_mutation(value: I256, mutated: Option<I256>) {
613            assert!(
614                mutated.is_none() || mutated.is_some_and(|m| m != value),
615                "Mutation failed: value = {value:?}, mutated = {mutated:?}"
616            );
617        }
618
619        for value in test_values {
620            for _ in 0..100 {
621                validate_mutation(value, I256::increment_decrement(value, size, &mut runner));
622                validate_mutation(value, I256::flip_random_bit(value, size, &mut runner));
623                validate_mutation(value, I256::mutate_interesting_byte(value, size, &mut runner));
624                validate_mutation(value, I256::mutate_interesting_word(value, size, &mut runner));
625                validate_mutation(value, I256::mutate_interesting_dword(value, size, &mut runner));
626            }
627        }
628    }
629
630    #[test]
631    fn test_mutate_address() {
632        let mut runner = TestRunner::new(Config::default());
633        let value = Address::random();
634
635        #[track_caller]
636        fn validate_mutation(value: Address, mutated: Option<Address>) {
637            assert!(
638                mutated.is_none() || mutated.is_some_and(|mutated| mutated != value),
639                "Mutation failed for value: {value:?}, result: {mutated:?}"
640            );
641        }
642
643        for _ in 0..100 {
644            validate_mutation(value, Address::flip_random_bit(value, 20, &mut runner));
645            validate_mutation(value, Address::mutate_interesting_byte(value, 20, &mut runner));
646            validate_mutation(value, Address::mutate_interesting_word(value, 20, &mut runner));
647            validate_mutation(value, Address::mutate_interesting_dword(value, 20, &mut runner));
648        }
649    }
650
651    #[test]
652    fn test_mutate_word() {
653        let mut runner = TestRunner::new(Config::default());
654        let value = Word::random();
655
656        #[track_caller]
657        fn validate_mutation(value: Word, mutated: Option<Word>) {
658            assert!(
659                mutated.is_none() || mutated.is_some_and(|mutated| mutated != value),
660                "Mutation failed for value: {value:?}, result: {mutated:?}"
661            );
662        }
663
664        for _ in 0..100 {
665            validate_mutation(value, Word::flip_random_bit(value, 32, &mut runner));
666            validate_mutation(value, Word::mutate_interesting_byte(value, 32, &mut runner));
667            validate_mutation(value, Word::mutate_interesting_word(value, 32, &mut runner));
668            validate_mutation(value, Word::mutate_interesting_dword(value, 32, &mut runner));
669        }
670    }
671
672    #[test]
673    fn test_mutate_interesting_word_too_small_returns_none() {
674        let mut runner = TestRunner::new(Config::default());
675        let value = U256::from(123);
676        assert!(U256::mutate_interesting_word(value, 8, &mut runner).is_none());
677    }
678
679    #[test]
680    fn test_mutate_interesting_dword_too_small_returns_none() {
681        let mut runner = TestRunner::new(Config::default());
682        let value = I256::from_dec_str("123").unwrap();
683        assert!(I256::mutate_interesting_dword(value, 16, &mut runner).is_none());
684    }
685
686    #[test]
687    fn test_u256_bound() {
688        let mut runner = TestRunner::new(Config::default());
689        let min = U256::from(0u64);
690        let max = U256::from(200u64);
691        let original = U256::from(100u64);
692
693        for _ in 0..50 {
694            let result = original.bound(min, max, &mut runner);
695            assert!(result.is_some(), "Mutation should occur");
696
697            let mutated = result.unwrap();
698            assert!(mutated >= min, "Mutated value >= min");
699            assert!(mutated <= max, "Mutated value <= max");
700            assert_ne!(mutated, original, "mutated value should differ from original");
701        }
702
703        // Test bound in [min, max] range.
704        let result = original.bound(U256::MIN, U256::MAX, &mut runner);
705        assert!(result.is_some(), "Mutation should occur");
706    }
707
708    #[test]
709    fn test_i256_bound() {
710        let mut runner = TestRunner::new(Config::default());
711        let min = I256::from_dec_str("-100").unwrap();
712        let max = I256::from_dec_str("100").unwrap();
713        let original = I256::from_dec_str("10").unwrap();
714
715        for _ in 0..50 {
716            let result = original.bound(min, max, &mut runner);
717            assert!(result.is_some(), "Mutation should occur");
718
719            let mutated = result.unwrap();
720            assert!(mutated >= min, "Mutated value >= min");
721            assert!(mutated <= max, "Mutated value <= max");
722            assert_ne!(mutated, original, "Mutated value should not equal current");
723        }
724
725        // Test bound in [min, max] range.
726        let result = original.bound(I256::MIN, I256::MAX, &mut runner);
727        assert!(result.is_some(), "Mutation should occur");
728    }
729}