Fix error-handling for i2a_ASN1_OBJECT.

Some BIO_write failures weren't handled. Otherwise would successfully
write truncated results. The other i2a functions all report -1 on
truncation, so match. While I'm here, write a test to make sure I didn't
break this.

Change-Id: If17d0209e75c15b3f37bceb1cdfb480fd2c62c4d
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/49931
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/asn1/a_object.c b/crypto/asn1/a_object.c
index b88f06b..0de3141 100644
--- a/crypto/asn1/a_object.c
+++ b/crypto/asn1/a_object.c
@@ -110,26 +110,37 @@
     return OBJ_obj2txt(buf, buf_len, a, 0);
 }
 
+static int write_str(BIO *bp, const char *str)
+{
+    int len = strlen(str);
+    return BIO_write(bp, str, len) == len ? len : -1;
+}
+
 int i2a_ASN1_OBJECT(BIO *bp, const ASN1_OBJECT *a)
 {
-    char buf[80], *p = buf;
-    int i;
-
-    if ((a == NULL) || (a->data == NULL))
-        return (BIO_write(bp, "NULL", 4));
-    i = i2t_ASN1_OBJECT(buf, sizeof buf, a);
-    if (i > (int)(sizeof(buf) - 1)) {
-        p = OPENSSL_malloc(i + 1);
-        if (!p)
-            return -1;
-        i2t_ASN1_OBJECT(p, i + 1, a);
+    if (a == NULL || a->data == NULL) {
+        return write_str(bp, "NULL");
     }
-    if (i <= 0)
-        return BIO_write(bp, "<INVALID>", 9);
-    BIO_write(bp, p, i);
-    if (p != buf)
-        OPENSSL_free(p);
-    return (i);
+
+    char buf[80], *allocated = NULL;
+    const char *str = buf;
+    int len = i2t_ASN1_OBJECT(buf, sizeof(buf), a);
+    if (len > (int)sizeof(buf) - 1) {
+        /* The input was truncated. Allocate a buffer that fits. */
+        allocated = OPENSSL_malloc(len + 1);
+        if (allocated == NULL) {
+            return -1;
+        }
+        len = i2t_ASN1_OBJECT(allocated, len + 1, a);
+        str = allocated;
+    }
+    if (len <= 0) {
+        str = "<INVALID>";
+    }
+
+    int ret = write_str(bp, str);
+    OPENSSL_free(allocated);
+    return ret;
 }
 
 ASN1_OBJECT *d2i_ASN1_OBJECT(ASN1_OBJECT **a, const unsigned char **pp,
diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc
index bb2b0a8..1381169 100644
--- a/crypto/asn1/asn1_test.cc
+++ b/crypto/asn1/asn1_test.cc
@@ -1587,6 +1587,88 @@
   }
 }
 
+TEST(ASN1Test, PrintASN1Object) {
+  const struct {
+    std::vector<uint8_t> in;
+    const char *expected;
+  } kDataTests[] = {
+      // Known OIDs print as the name.
+      {{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01}, "rsaEncryption"},
+
+      // Unknown OIDs print in decimal.
+      {{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x00},
+       "1.2.840.113554.4.1.72585.0"},
+
+      // Inputs which cannot be parsed as OIDs print as "<INVALID>".
+      {{0xff}, "<INVALID>"},
+
+      // The function has an internal 80-byte buffer. Test inputs at that
+      // boundary. First, 78 characters.
+      {{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7,
+        0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+       "1.2.840.113554.4.1.72585.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0."
+       "0.0.0.1"},
+      // 79 characters.
+      {{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7,
+        0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a},
+       "1.2.840.113554.4.1.72585.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0."
+       "0.0.0.10"},
+      // 80 characters.
+      {{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7,
+        0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64},
+       "1.2.840.113554.4.1.72585.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0."
+       "0.0.0.100"},
+      // 81 characters.
+      {{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7,
+        0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x68},
+       "1.2.840.113554.4.1.72585.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0."
+       "0.0.0.1000"},
+      // 82 characters.
+      {{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7,
+        0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xce, 0x10},
+       "1.2.840.113554.4.1.72585.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0."
+       "0.0.0.10000"},
+  };
+  for (const auto &t : kDataTests) {
+    SCOPED_TRACE(Bytes(t.in));
+    bssl::UniquePtr<ASN1_OBJECT> obj(ASN1_OBJECT_create(
+        NID_undef, t.in.data(), t.in.size(), /*sn=*/nullptr, /*ln=*/nullptr));
+    ASSERT_TRUE(obj);
+    bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+    ASSERT_TRUE(bio);
+
+    int len = i2a_ASN1_OBJECT(bio.get(), obj.get());
+    EXPECT_EQ(len, static_cast<int>(strlen(t.expected)));
+
+    const uint8_t *bio_data;
+    size_t bio_len;
+    BIO_mem_contents(bio.get(), &bio_data, &bio_len);
+    EXPECT_EQ(t.expected,
+              std::string(reinterpret_cast<const char *>(bio_data), bio_len));
+  }
+
+  // Test writing NULL.
+  bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+  ASSERT_TRUE(bio);
+  int len = i2a_ASN1_OBJECT(bio.get(), nullptr);
+  EXPECT_EQ(len, 4);
+  const uint8_t *bio_data;
+  size_t bio_len;
+  BIO_mem_contents(bio.get(), &bio_data, &bio_len);
+  EXPECT_EQ("NULL",
+            std::string(reinterpret_cast<const char *>(bio_data), bio_len));
+}
+
 // The ASN.1 macros do not work on Windows shared library builds, where usage of
 // |OPENSSL_EXPORT| is a bit stricter.
 #if !defined(OPENSSL_WINDOWS) || !defined(BORINGSSL_SHARED_LIBRARY)