[dart:io] Fix iOS https leaks

Add move constructor and equality operators to
ScopedCFType, add specialized destructor for arrays.

Add release to ScopedCFType.

Use ScopedCFType for SecCertificateRef.

Bug: https://github.com/dart-lang/sdk/issues/36935
Change-Id: I7b40f23fb8f0d7ab0835587e040362f883f86bd5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/102560
Reviewed-by: Zach Anderson <zra@google.com>
Auto-Submit: Dan Field <dnfield@google.com>
Commit-Queue: Zach Anderson <zra@google.com>
diff --git a/runtime/bin/security_context_macos.cc b/runtime/bin/security_context_macos.cc
index 2964d67..a9afb58 100644
--- a/runtime/bin/security_context_macos.cc
+++ b/runtime/bin/security_context_macos.cc
@@ -38,8 +38,18 @@
   T* ptr() { return &obj_; }
   const T get() const { return obj_; }
 
+  DART_WARN_UNUSED_RESULT T release() {
+    T temp = obj_;
+    obj_ = NULL;
+    return temp;
+  }
+
   void set(T obj) { obj_ = obj; }
 
+  bool operator==(T other) { return other == get(); }
+
+  bool operator!=(T other) { return other != get(); }
+
  private:
   T obj_;
 
@@ -47,6 +57,20 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedCFType);
 };
 
+static void releaseObjects(const void* val, void* context) {
+  CFRelease(val);
+}
+
+template <>
+ScopedCFType<CFMutableArrayRef>::~ScopedCFType() {
+  if (obj_ != NULL) {
+    CFIndex count = 0;
+    CFArrayApplyFunction(obj_, CFRangeMake(0, CFArrayGetCount(obj_)),
+                         releaseObjects, &count);
+    CFRelease(obj_);
+  }
+}
+
 typedef ScopedCFType<CFMutableArrayRef> ScopedCFMutableArrayRef;
 typedef ScopedCFType<CFDataRef> ScopedCFDataRef;
 typedef ScopedCFType<CFStringRef> ScopedCFStringRef;
@@ -60,10 +84,10 @@
   }
   int length = i2d_X509(cert, NULL);
   if (length < 0) {
-    return 0;
+    return NULL;
   }
-  auto deb_cert = std::unique_ptr<unsigned char[]>(new unsigned char[length]);
-  unsigned char* temp = deb_cert.get();
+  auto deb_cert = std::make_unique<unsigned char[]>(length);
+  auto temp = deb_cert.get();
   if (i2d_X509(cert, &temp) != length) {
     return NULL;
   }
@@ -72,12 +96,7 @@
   // Implementation here:
   // https://opensource.apple.com/source/libsecurity_keychain/libsecurity_keychain-55050.2/lib/SecCertificate.cpp.auto.html
   ScopedCFDataRef cert_buf(CFDataCreate(NULL, deb_cert.get(), length));
-  SecCertificateRef auth_cert =
-      SecCertificateCreateWithData(NULL, cert_buf.get());
-  if (auth_cert == NULL) {
-    return NULL;
-  }
-  return auth_cert;
+  return SecCertificateCreateWithData(NULL, cert_buf.get());
 }
 
 static int CertificateVerificationCallback(X509_STORE_CTX* ctx, void* arg) {
@@ -93,11 +112,11 @@
     cert_chain.set(CFArrayCreateMutable(NULL, num_certs, NULL));
     X509* ca;
     while ((ca = sk_X509_shift(user_provided_certs)) != NULL) {
-      SecCertificateRef cert = CreateSecCertificateFromX509(ca);
+      ScopedSecCertificateRef cert(CreateSecCertificateFromX509(ca));
       if (cert == NULL) {
         return ctx->verify_cb(0, ctx);
       }
-      CFArrayAppendValue(cert_chain.get(), cert);
+      CFArrayAppendValue(cert_chain.get(), cert.release());
       ++current_cert;
 
       if (current_cert == num_certs) {
@@ -115,11 +134,11 @@
   if (store->objs != NULL) {
     for (uintptr_t i = 0; i < sk_X509_OBJECT_num(store->objs); ++i) {
       X509* ca = sk_X509_OBJECT_value(store->objs, i)->data.x509;
-      SecCertificateRef cert = CreateSecCertificateFromX509(ca);
+      ScopedSecCertificateRef cert(CreateSecCertificateFromX509(ca));
       if (cert == NULL) {
         return ctx->verify_cb(0, ctx);
       }
-      CFArrayAppendValue(trusted_certs.get(), cert);
+      CFArrayAppendValue(trusted_certs.get(), cert.release());
     }
   }