Repository: alexcrichton/toml-rs
Pull request #183 (2017-05-30 23:34:17)

Better invalid number handling
This improves handling of invalid numbers (e.g. `float` or `inf`) by:
- Return an error when deserializing huge numbers like `9e99999` instead of silently deserializing them into `inf`
- Return an error serializing a non-finite number (`nan`, `+inf`, or `-inf`).

Unfortunately, I also ended up adding a variant to the public `toml::se::Error` enum, so this is a breaking change and would need a version bump.

Addresses https://github.com/alexcrichton/toml-rs/issues/181


Patch:
diff --git a/src/de.rs b/src/de.rs
index bd4ead0..d5af272 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -893,6 +893,12 @@ impl<'a> Deserializer<'a> {
         }
         number.parse().map_err(|_e| {
             self.error(start, ErrorKind::NumberInvalid)
+        }).and_then(|n: f64| {
+            if n.is_finite() {
+                Ok(n)
+            } else {
+                Err(self.error(start, ErrorKind::NumberInvalid))
+            }
         })
     }
 
diff --git a/src/ser.rs b/src/ser.rs
index 9af304f..97febb5 100644
--- a/src/ser.rs
+++ b/src/ser.rs
@@ -80,7 +80,7 @@ pub fn to_vec<T: ?Sized>(value: &T) -> Result<Vec<u8>, Error>
 ///             enabled: false,
 ///         },
 ///     };
-/// 
+///
 ///     let toml = toml::to_string(&config).unwrap();
 ///     println!("{}", toml)
 /// }
@@ -122,6 +122,9 @@ pub enum Error {
     /// A serialized date was invalid.
     DateInvalid,
 
+    /// A serialized number was invalid.
+    NumberInvalid,
+
     /// None was attempted to be serialized, but it's not supported.
     UnsupportedNone,
 
@@ -391,6 +394,10 @@ impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> {
     }
 
     fn serialize_f32(mut self, v: f32) -> Result<(), Self::Error> {
+        if !v.is_finite() {
+            return Err(Error::NumberInvalid);
+        }
+
         self.emit_key("float")?;
         drop(write!(self.dst, "{}", v));
         if v % 1.0 == 0.0 {
@@ -403,6 +410,10 @@ impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> {
     }
 
     fn serialize_f64(mut self, v: f64) -> Result<(), Self::Error> {
+        if !v.is_finite() {
+            return Err(Error::NumberInvalid);
+        }
+
         self.emit_key("float")?;
         drop(write!(self.dst, "{}", v));
         if v % 1.0 == 0.0 {
@@ -1019,7 +1030,8 @@ impl fmt::Display for Error {
             Error::KeyNewline => "map keys cannot contain newlines".fmt(f),
             Error::ArrayMixedType => "arrays cannot have mixed types".fmt(f),
             Error::ValueAfterTable => "values must be emitted before tables".fmt(f),
-            Error::DateInvalid => "a serialize date was invalid".fmt(f),
+            Error::DateInvalid => "a serialized date was invalid".fmt(f),
+            Error::NumberInvalid => "a serialized number was invalid".fmt(f),
             Error::UnsupportedNone => "unsupported None value".fmt(f),
             Error::Custom(ref s) => s.fmt(f),
             Error::__Nonexhaustive => panic!(),
@@ -1036,6 +1048,7 @@ impl error::Error for Error {
             Error::ArrayMixedType => "arrays cannot have mixed types",
             Error::ValueAfterTable => "values must be emitted before tables",
             Error::DateInvalid => "a serialized date was invalid",
+            Error::NumberInvalid => "a serialized number was invalid",
             Error::UnsupportedNone => "unsupported None value",
             Error::Custom(_) => "custom error",
             Error::__Nonexhaustive => panic!(),


Test patch:
diff --git a/tests/invalid-encoder-misc.rs b/tests/invalid-encoder-misc.rs
new file mode 100644
index 0000000..272f58f
--- /dev/null
+++ b/tests/invalid-encoder-misc.rs
@@ -0,0 +1,14 @@
+extern crate toml;
+
+use std::f64;
+
+#[test]
+fn test_invalid_float_encode() {
+    fn bad(value: toml::Value) {
+        assert!(toml::to_string(&value).is_err());
+    }
+
+    bad(toml::Value::Float(f64::INFINITY));
+    bad(toml::Value::Float(f64::NEG_INFINITY));
+    bad(toml::Value::Float(f64::NAN));
+}
diff --git a/tests/invalid-misc.rs b/tests/invalid-misc.rs
index 53f625e..bb70b97 100644
--- a/tests/invalid-misc.rs
+++ b/tests/invalid-misc.rs
@@ -10,4 +10,8 @@ fn bad() {
     bad("a = 1__1");
     bad("a = 1_");
     bad("''");
+    bad("a = nan");
+    bad("a = -inf");
+    bad("a = inf");
+    bad("a = 9e99999");
 }