CVE-2021-32823: Other (NVD-CWE-Other)
value: In the bindata RubyGem before version 2.4.10 there is a potential denial-of-service vulnerability. In affected versions it is very slow for certain classes in BinData to be created. For example BinData::Bit100000, BinData::Bit100001, BinData::Bit100002, BinData::Bit<N>. In combination with <user_input>.constantize there is a potential for a CPU-based DoS. In version 2.4.10 bindata improved the creation time of Bits and Integers.
CWE description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
CVSS v3 base score: 3.7
Published: 2021-06-24T00:15Z
Repository: https://github.com/dmendel/bindata
Language: Ruby
Files changed: ChangeLog.rdoc, lib/bindata/bits.rb, lib/bindata/int.rb
Commit date: 2021-05-18 11:39:35 +0800
Commit message: Improved creation time of Bits and Integers

Diff: diff --git a/ChangeLog.rdoc b/ChangeLog.rdoc
index dbc08d2..d7a7458 100644
--- a/ChangeLog.rdoc
+++ b/ChangeLog.rdoc
@@ -1,7 +1,11 @@
 = BinData Changelog
 
+== Version 2.4.10 (2021-05-18)
+
+* Improve speed of dynamic object creation.  Reported by Charlie Ablett.
+
 == Version 2.4.9 (2021-04-22)
 
 * Change example from Fixnum to Integer.  Thanks to Tim Chambers.
 * Now works with frozen string literals.  Requested by Jeremy Evans.
 
diff --git a/lib/bindata/bits.rb b/lib/bindata/bits.rb
index b04c4fb..1a69d5f 100644
--- a/lib/bindata/bits.rb
+++ b/lib/bindata/bits.rb
@@ -113,18 +113,18 @@ module BinData
         if nbits == 1 && signed == :signed
           raise "signed bitfield must have more than one bit"
         end
 
         if signed == :signed
-          max = (1 << (nbits - 1)) - 1
-          min = -(max + 1)
+          max = "max = (1 << (#{nbits} - 1)) - 1"
+          min = "min = -(max + 1)"
         else
-          min = 0
-          max = (1 << nbits) - 1
+          min = "min = 0"
+          max = "max = (1 << #{nbits}) - 1"
         end
 
-        clamp = "(val < #{min}) ? #{min} : (val > #{max}) ? #{max} : val"
+        clamp = "(#{max}; #{min}; val = (val < min) ? min : (val > max) ? max : val)"
 
         if nbits == 1
           # allow single bits to be used as booleans
           clamp = "(val == true) ? 1 : (not val) ? 0 : #{clamp}"
         end
diff --git a/lib/bindata/int.rb b/lib/bindata/int.rb
index 0fc0c20..e9c087b 100644
--- a/lib/bindata/int.rb
+++ b/lib/bindata/int.rb
@@ -57,18 +57,20 @@ module BinData
       #-------------
       private
 
       def create_clamp_code(nbits, signed)
         if signed == :signed
-          max = (1 << (nbits - 1)) - 1
-          min = -(max + 1)
+          max = "max = (1 << (#{nbits} - 1)) - 1"
+          min = "min = -(max + 1)"
         else
-          max = (1 << nbits) - 1
-          min = 0
+          max = "max = (1 << #{nbits}) - 1"
+          min = "min = 0"
         end
 
-        "val = (val < #{min}) ? #{min} : (val > #{max}) ? #{max} : val"
+        clamp = "(#{max}; #{min}; val = (val < min) ? min : (val > max) ? max : val)"
+
+        "val = #{clamp}"
       end
 
       def create_read_code(nbits, endian, signed)
         read_str = create_raw_read_code(nbits, endian, signed)
 

Vulnerable code:           max = (1 << (nbits - 1)) - 1
          min = -(max + 1)
          min = 0
          max = (1 << nbits) - 1
        clamp = "(val < #{min}) ? #{min} : (val > #{max}) ? #{max} : val"
          max = (1 << (nbits - 1)) - 1
          min = -(max + 1)
          max = (1 << nbits) - 1
          min = 0
        "val = (val < #{min}) ? #{min} : (val > #{max}) ? #{max} : val"
Fixed code: == Version 2.4.10 (2021-05-18)

* Improve speed of dynamic object creation.  Reported by Charlie Ablett.

          max = "max = (1 << (#{nbits} - 1)) - 1"
          min = "min = -(max + 1)"
          min = "min = 0"
          max = "max = (1 << #{nbits}) - 1"
        clamp = "(#{max}; #{min}; val = (val < min) ? min : (val > max) ? max : val)"
          max = "max = (1 << (#{nbits} - 1)) - 1"
          min = "min = -(max + 1)"
          max = "max = (1 << #{nbits}) - 1"
          min = "min = 0"
        clamp = "(#{max}; #{min}; val = (val < min) ? min : (val > max) ? max : val)"

        "val = #{clamp}"