Merge remote-tracking branch 'public/pr/1771' into mbedtls-2.1
diff --git a/.travis.yml b/.travis.yml
index f30a4e3..91a36c9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,6 +9,7 @@
 - tests/scripts/check-generated-files.sh
 - tests/scripts/check-doxy-blocks.pl
 - tests/scripts/check-names.sh
+- tests/scripts/check-files.py
 - cmake -D CMAKE_BUILD_TYPE:String="Check" .
 - make
 - make test
diff --git a/ChangeLog b/ChangeLog
index f05ea53..e77f935 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 mbed TLS ChangeLog (Sorted per branch, date)
 
+= mbed TLS x.x.x branch released xxxx-xx-xx
+
+Bugfix
+   * Fix a memory leak in mbedtls_x509_csr_parse(), found by catenacyber,
+     Philippe Antoine.
+   * Clarify documentation for mbedtls_ssl_write() to include 0 as a valid
+     return value. Found by @davidwu2000. #839
+
+Changes
+   * Change the shebang line in Perl scripts to look up perl in the PATH.
+     Contributed by fbrosson in #1533.
+
 = mbed TLS 2.1.13 branch released 2018-06-18
 
 Bugfix
diff --git a/README.md b/README.md
index 38a7d9b..657220a 100644
--- a/README.md
+++ b/README.md
@@ -197,8 +197,8 @@
 
 ### Process
 
-1.  [Check for open issues](https://github.com/ARMmbed/mbedtls/issues) or [start a discussion](https://tls.mbed.org/discussions) around a feature idea or a bug.
-2.  Fork the [mbed TLS repository on GitHub](https://github.com/ARMmbed/mbedtls) to start making your changes. As a general rule, you should use the "development" branch as a basis.
+1.  [Check for open issues](https://github.com/ARMmbed/mbedtls/issues) or [start a discussion](https://forums.mbed.com/c/mbed-tls) around a feature idea or a bug.
+2.  Fork the [Mbed TLS repository on GitHub](https://github.com/ARMmbed/mbedtls) to start making your changes. As a general rule, you should use the "development" branch as a basis.
 3.  Write a test which shows that the bug was fixed or that the feature works as expected.
 4.  Send a pull request and bug us until it gets merged and published. We will include your name in the ChangeLog :)
 
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index eeb645c..834afe6 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -2213,17 +2213,19 @@
  *                 or MBEDTLS_ERR_SSL_WANT_WRITE of MBEDTLS_ERR_SSL_WANT_READ,
  *                 or another negative error code.
  *
- * \note           If this function returns something other than a positive
- *                 value or MBEDTLS_ERR_SSL_WANT_READ/WRITE, the ssl context
- *                 becomes unusable, and you should either free it or call
- *                 \c mbedtls_ssl_session_reset() on it before re-using it for
- *                 a new connection; the current connection must be closed.
+ * \note           If this function returns something other than 0, a positive
+ *                 value or MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop
+ *                 using the SSL context for reading or writing, and either
+ *                 free it or call \c mbedtls_ssl_session_reset() on it before
+ *                 re-using it for a new connection; the current connection
+ *                 must be closed.
  *
  * \note           When this function returns MBEDTLS_ERR_SSL_WANT_WRITE/READ,
  *                 it must be called later with the *same* arguments,
- *                 until it returns a positive value. When the function returns
- *                 MBEDTLS_ERR_SSL_WANT_WRITE there may be some partial
- *                 data in the output buffer, however this is not yet sent.
+ *                 until it returns a value greater that or equal to 0. When
+ *                 the function returns MBEDTLS_ERR_SSL_WANT_WRITE there may be
+ *                 some partial data in the output buffer, however this is not
+ *                 yet sent.
  *
  * \note           If the requested length is greater than the maximum
  *                 fragment length (either the built-in limit or the one set
@@ -2232,6 +2234,9 @@
  *                 - with DTLS, MBEDTLS_ERR_SSL_BAD_INPUT_DATA is returned.
  *                 \c mbedtls_ssl_get_max_frag_len() may be used to query the
  *                 active maximum fragment length.
+ *
+ * \note           Attempting to write 0 bytes will result in an empty TLS
+ *                 application record being sent.
  */
 int mbedtls_ssl_write( mbedtls_ssl_context *ssl, const unsigned char *buf, size_t len );
 
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 2cb6542..e3c851e 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -6884,8 +6884,16 @@
 }
 
 /*
- * Send application data to be encrypted by the SSL layer,
- * taking care of max fragment length and buffer size
+ * Send application data to be encrypted by the SSL layer, taking care of max
+ * fragment length and buffer size.
+ *
+ * According to RFC 5246 Section 6.2.1:
+ *
+ *      Zero-length fragments of Application data MAY be sent as they are
+ *      potentially useful as a traffic analysis countermeasure.
+ *
+ * Therefore, it is possible that the input message length is 0 and the
+ * corresponding return code is 0 on success.
  */
 static int ssl_write_real( mbedtls_ssl_context *ssl,
                            const unsigned char *buf, size_t len )
@@ -6913,6 +6921,12 @@
 
     if( ssl->out_left != 0 )
     {
+        /*
+         * The user has previously tried to send the data and
+         * MBEDTLS_ERR_SSL_WANT_WRITE or the message was only partially
+         * written. In this case, we expect the high-level write function
+         * (e.g. mbedtls_ssl_write()) to be called with the same parameters
+         */
         if( ( ret = mbedtls_ssl_flush_output( ssl ) ) != 0 )
         {
             MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_flush_output", ret );
@@ -6921,6 +6935,11 @@
     }
     else
     {
+        /*
+         * The user is trying to send a message the first time, so we need to
+         * copy the data into the internal buffers and setup the data structure
+         * to keep track of partial writes
+         */
         ssl->out_msglen  = len;
         ssl->out_msgtype = MBEDTLS_SSL_MSG_APPLICATION_DATA;
         memcpy( ssl->out_msg, buf, len );
diff --git a/library/x509_csr.c b/library/x509_csr.c
index fc171cb..22989a2 100644
--- a/library/x509_csr.c
+++ b/library/x509_csr.c
@@ -271,34 +271,25 @@
         return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
 
 #if defined(MBEDTLS_PEM_PARSE_C)
-    mbedtls_pem_init( &pem );
-
     /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */
-    if( buflen == 0 || buf[buflen - 1] != '\0' )
-        ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT;
-    else
+    if( buf[buflen - 1] == '\0' )
+    {
+        mbedtls_pem_init( &pem );
         ret = mbedtls_pem_read_buffer( &pem,
                                "-----BEGIN CERTIFICATE REQUEST-----",
                                "-----END CERTIFICATE REQUEST-----",
                                buf, NULL, 0, &use_len );
 
-    if( ret == 0 )
-    {
-        /*
-         * Was PEM encoded, parse the result
-         */
-        if( ( ret = mbedtls_x509_csr_parse_der( csr, pem.buf, pem.buflen ) ) != 0 )
-            return( ret );
+        if( ret == 0 )
+            /*
+             * Was PEM encoded, parse the result
+             */
+            ret = mbedtls_x509_csr_parse_der( csr, pem.buf, pem.buflen );
 
         mbedtls_pem_free( &pem );
-        return( 0 );
+        if( ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT )
+            return( ret );
     }
-    else if( ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT )
-    {
-        mbedtls_pem_free( &pem );
-        return( ret );
-    }
-    else
 #endif /* MBEDTLS_PEM_PARSE_C */
     return( mbedtls_x509_csr_parse_der( csr, buf, buflen ) );
 }
diff --git a/scripts/config.pl b/scripts/config.pl
index dcf0281..d9da997 100755
--- a/scripts/config.pl
+++ b/scripts/config.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 #
 # This file is part of mbed TLS (https://tls.mbed.org)
 #
diff --git a/scripts/generate_errors.pl b/scripts/generate_errors.pl
index cc9527e..3687bd4 100755
--- a/scripts/generate_errors.pl
+++ b/scripts/generate_errors.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 
 # Generate error.c
 #
diff --git a/scripts/generate_features.pl b/scripts/generate_features.pl
index 2aa695c..1bd82ca 100755
--- a/scripts/generate_features.pl
+++ b/scripts/generate_features.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 #
 
 use strict;
diff --git a/scripts/generate_visualc_files.pl b/scripts/generate_visualc_files.pl
index e042a44..811c71f 100755
--- a/scripts/generate_visualc_files.pl
+++ b/scripts/generate_visualc_files.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 
 # Generate main file, individual apps and solution files for MS Visual Studio
 # 2010
diff --git a/scripts/massif_max.pl b/scripts/massif_max.pl
index d1ce4ca..4e3342a 100755
--- a/scripts/massif_max.pl
+++ b/scripts/massif_max.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 
 # Parse a massif.out.xxx file and output peak total memory usage
 
diff --git a/scripts/rename.pl b/scripts/rename.pl
index c169078..22a86cc 100755
--- a/scripts/rename.pl
+++ b/scripts/rename.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 
 # rename identifiers (functions, types, enum constant, etc)
 # on upgrades of major version according to a list
diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh
index 6f2bfad..5c1ae2d 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -391,6 +391,10 @@
 msg "test: doxygen markup outside doxygen blocks" # < 1s
 tests/scripts/check-doxy-blocks.pl
 
+msg "test: check-files.py" # < 1s
+cleanup
+tests/scripts/check-files.py
+
 msg "test/build: declared and exported names" # < 3s
 cleanup
 tests/scripts/check-names.sh
@@ -502,6 +506,24 @@
 msg "test: compat.sh RC4, DES & NULL (full config)" # ~ 2 min
 if_build_succeeded env OPENSSL_CMD="$OPENSSL_LEGACY" GNUTLS_CLI="$GNUTLS_LEGACY_CLI" GNUTLS_SERV="$GNUTLS_LEGACY_SERV" tests/compat.sh -e '3DES\|DES-CBC3' -f 'NULL\|DES\|RC4\|ARCFOUR'
 
+msg "build: make, full config + DEPRECATED_WARNING, gcc -O" # ~ 30s
+cleanup
+cp "$CONFIG_H" "$CONFIG_BAK"
+scripts/config.pl full
+scripts/config.pl set MBEDTLS_DEPRECATED_WARNING
+# Build with -O -Wextra to catch a maximum of issues.
+make CC=gcc CFLAGS='-O -Werror -Wall -Wextra' lib programs
+make CC=gcc CFLAGS='-O -Werror -Wall -Wextra -Wno-unused-function' tests
+
+msg "build: make, full config + DEPRECATED_REMOVED, clang -O" # ~ 30s
+# No cleanup, just tweak the configuration and rebuild
+make clean
+scripts/config.pl unset MBEDTLS_DEPRECATED_WARNING
+scripts/config.pl set MBEDTLS_DEPRECATED_REMOVED
+# Build with -O -Wextra to catch a maximum of issues.
+make CC=clang CFLAGS='-O -Werror -Wall -Wextra' lib programs
+make CC=clang CFLAGS='-O -Werror -Wall -Wextra -Wno-unused-function' tests
+
 msg "test/build: curves.pl (gcc)" # ~ 4 min
 cleanup
 record_status tests/scripts/curves.pl
diff --git a/tests/scripts/check-doxy-blocks.pl b/tests/scripts/check-doxy-blocks.pl
index b0fd696..4967699 100755
--- a/tests/scripts/check-doxy-blocks.pl
+++ b/tests/scripts/check-doxy-blocks.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 
 # Detect comment blocks that are likely meant to be doxygen blocks but aren't.
 #
diff --git a/tests/scripts/check-files.py b/tests/scripts/check-files.py
new file mode 100755
index 0000000..f560d03
--- /dev/null
+++ b/tests/scripts/check-files.py
@@ -0,0 +1,223 @@
+#!/usr/bin/env python3
+"""
+This file is part of Mbed TLS (https://tls.mbed.org)
+
+Copyright (c) 2018, Arm Limited, All Rights Reserved
+
+Purpose
+
+This script checks the current state of the source code for minor issues,
+including incorrect file permissions, presence of tabs, non-Unix line endings,
+trailing whitespace, presence of UTF-8 BOM, and TODO comments.
+Note: requires python 3, must be run from Mbed TLS root.
+"""
+
+import os
+import argparse
+import logging
+import codecs
+import sys
+
+
+class IssueTracker(object):
+    """Base class for issue tracking. Issues should inherit from this and
+    overwrite either issue_with_line if they check the file line by line, or
+    overwrite check_file_for_issue if they check the file as a whole."""
+
+    def __init__(self):
+        self.heading = ""
+        self.files_exemptions = []
+        self.files_with_issues = {}
+
+    def should_check_file(self, filepath):
+        for files_exemption in self.files_exemptions:
+            if filepath.endswith(files_exemption):
+                return False
+        return True
+
+    def issue_with_line(self, line):
+        raise NotImplementedError
+
+    def check_file_for_issue(self, filepath):
+        with open(filepath, "rb") as f:
+            for i, line in enumerate(iter(f.readline, b"")):
+                self.check_file_line(filepath, line, i + 1)
+
+    def check_file_line(self, filepath, line, line_number):
+        if self.issue_with_line(line):
+            if filepath not in self.files_with_issues.keys():
+                self.files_with_issues[filepath] = []
+            self.files_with_issues[filepath].append(line_number)
+
+    def output_file_issues(self, logger):
+        if self.files_with_issues.values():
+            logger.info(self.heading)
+            for filename, lines in sorted(self.files_with_issues.items()):
+                if lines:
+                    logger.info("{}: {}".format(
+                        filename, ", ".join(str(x) for x in lines)
+                    ))
+                else:
+                    logger.info(filename)
+            logger.info("")
+
+
+class PermissionIssueTracker(IssueTracker):
+
+    def __init__(self):
+        super().__init__()
+        self.heading = "Incorrect permissions:"
+
+    def check_file_for_issue(self, filepath):
+        if not (os.access(filepath, os.X_OK) ==
+                filepath.endswith((".sh", ".pl", ".py"))):
+            self.files_with_issues[filepath] = None
+
+
+class EndOfFileNewlineIssueTracker(IssueTracker):
+
+    def __init__(self):
+        super().__init__()
+        self.heading = "Missing newline at end of file:"
+
+    def check_file_for_issue(self, filepath):
+        with open(filepath, "rb") as f:
+            if not f.read().endswith(b"\n"):
+                self.files_with_issues[filepath] = None
+
+
+class Utf8BomIssueTracker(IssueTracker):
+
+    def __init__(self):
+        super().__init__()
+        self.heading = "UTF-8 BOM present:"
+
+    def check_file_for_issue(self, filepath):
+        with open(filepath, "rb") as f:
+            if f.read().startswith(codecs.BOM_UTF8):
+                self.files_with_issues[filepath] = None
+
+
+class LineEndingIssueTracker(IssueTracker):
+
+    def __init__(self):
+        super().__init__()
+        self.heading = "Non Unix line endings:"
+
+    def issue_with_line(self, line):
+        return b"\r" in line
+
+
+class TrailingWhitespaceIssueTracker(IssueTracker):
+
+    def __init__(self):
+        super().__init__()
+        self.heading = "Trailing whitespace:"
+        self.files_exemptions = [".md"]
+
+    def issue_with_line(self, line):
+        return line.rstrip(b"\r\n") != line.rstrip()
+
+
+class TabIssueTracker(IssueTracker):
+
+    def __init__(self):
+        super().__init__()
+        self.heading = "Tabs present:"
+        self.files_exemptions = [
+            "Makefile", "generate_visualc_files.pl"
+        ]
+
+    def issue_with_line(self, line):
+        return b"\t" in line
+
+
+class TodoIssueTracker(IssueTracker):
+
+    def __init__(self):
+        super().__init__()
+        self.heading = "TODO present:"
+        self.files_exemptions = [
+            __file__, "benchmark.c", "pull_request_template.md"
+        ]
+
+    def issue_with_line(self, line):
+        return b"todo" in line.lower()
+
+
+class IntegrityChecker(object):
+
+    def __init__(self, log_file):
+        self.check_repo_path()
+        self.logger = None
+        self.setup_logger(log_file)
+        self.files_to_check = (
+            ".c", ".h", ".sh", ".pl", ".py", ".md", ".function", ".data",
+            "Makefile", "CMakeLists.txt", "ChangeLog"
+        )
+        self.issues_to_check = [
+            PermissionIssueTracker(),
+            EndOfFileNewlineIssueTracker(),
+            Utf8BomIssueTracker(),
+            LineEndingIssueTracker(),
+            TrailingWhitespaceIssueTracker(),
+            TabIssueTracker(),
+            TodoIssueTracker(),
+        ]
+
+    def check_repo_path(self):
+        if not all(os.path.isdir(d) for d in ["include", "library", "tests"]):
+            raise Exception("Must be run from Mbed TLS root")
+
+    def setup_logger(self, log_file, level=logging.INFO):
+        self.logger = logging.getLogger()
+        self.logger.setLevel(level)
+        if log_file:
+            handler = logging.FileHandler(log_file)
+            self.logger.addHandler(handler)
+        else:
+            console = logging.StreamHandler()
+            self.logger.addHandler(console)
+
+    def check_files(self):
+        for root, dirs, files in sorted(os.walk(".")):
+            for filename in sorted(files):
+                filepath = os.path.join(root, filename)
+                if (os.path.join("yotta", "module") in filepath or
+                        not filepath.endswith(self.files_to_check)):
+                    continue
+                for issue_to_check in self.issues_to_check:
+                    if issue_to_check.should_check_file(filepath):
+                        issue_to_check.check_file_for_issue(filepath)
+
+    def output_issues(self):
+        integrity_return_code = 0
+        for issue_to_check in self.issues_to_check:
+            if issue_to_check.files_with_issues:
+                integrity_return_code = 1
+            issue_to_check.output_file_issues(self.logger)
+        return integrity_return_code
+
+
+def run_main():
+    parser = argparse.ArgumentParser(
+        description=(
+            "This script checks the current state of the source code for "
+            "minor issues, including incorrect file permissions, "
+            "presence of tabs, non-Unix line endings, trailing whitespace, "
+            "presence of UTF-8 BOM, and TODO comments. "
+            "Note: requires python 3, must be run from Mbed TLS root."
+        )
+    )
+    parser.add_argument(
+        "-l", "--log_file", type=str, help="path to optional output log",
+    )
+    check_args = parser.parse_args()
+    integrity_check = IntegrityChecker(check_args.log_file)
+    integrity_check.check_files()
+    return_code = integrity_check.output_issues()
+    sys.exit(return_code)
+
+
+if __name__ == "__main__":
+    run_main()
diff --git a/tests/scripts/curves.pl b/tests/scripts/curves.pl
index c9554e0..f2008dc 100755
--- a/tests/scripts/curves.pl
+++ b/tests/scripts/curves.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 
 # curves.pl
 #
diff --git a/tests/scripts/depends-hashes.pl b/tests/scripts/depends-hashes.pl
index 29dcfb0..f57e7ed 100755
--- a/tests/scripts/depends-hashes.pl
+++ b/tests/scripts/depends-hashes.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 
 # depends-hashes.pl
 #
diff --git a/tests/scripts/depends-pkalgs.pl b/tests/scripts/depends-pkalgs.pl
index 14c92b2..97a43e8 100755
--- a/tests/scripts/depends-pkalgs.pl
+++ b/tests/scripts/depends-pkalgs.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 
 # depends-pkalgs.pl
 #
diff --git a/tests/scripts/gen_ctr_drbg.pl b/tests/scripts/gen_ctr_drbg.pl
index ee13024..3c074be 100755
--- a/tests/scripts/gen_ctr_drbg.pl
+++ b/tests/scripts/gen_ctr_drbg.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 #
 # Based on NIST CTR_DRBG.rsp validation file
 # Only uses AES-256-CTR cases that use a Derivation function
diff --git a/tests/scripts/gen_gcm_decrypt.pl b/tests/scripts/gen_gcm_decrypt.pl
index 6decac2..03809cb 100755
--- a/tests/scripts/gen_gcm_decrypt.pl
+++ b/tests/scripts/gen_gcm_decrypt.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 #
 # Based on NIST gcmDecryptxxx.rsp validation files
 # Only first 3 of every set used for compile time saving
diff --git a/tests/scripts/gen_gcm_encrypt.pl b/tests/scripts/gen_gcm_encrypt.pl
index 8adbbce..29ec677 100755
--- a/tests/scripts/gen_gcm_encrypt.pl
+++ b/tests/scripts/gen_gcm_encrypt.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 #
 # Based on NIST gcmEncryptIntIVxxx.rsp validation files
 # Only first 3 of every set used for compile time saving
diff --git a/tests/scripts/gen_pkcs1_v21_sign_verify.pl b/tests/scripts/gen_pkcs1_v21_sign_verify.pl
index 678e2f9..110cb4b 100755
--- a/tests/scripts/gen_pkcs1_v21_sign_verify.pl
+++ b/tests/scripts/gen_pkcs1_v21_sign_verify.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 #
 
 use strict;
diff --git a/tests/scripts/key-exchanges.pl b/tests/scripts/key-exchanges.pl
index d167c67..3bf7ae3 100755
--- a/tests/scripts/key-exchanges.pl
+++ b/tests/scripts/key-exchanges.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 
 # key-exchanges.pl
 #
diff --git a/tests/scripts/list-enum-consts.pl b/tests/scripts/list-enum-consts.pl
index 633e3fd..21c25b3 100755
--- a/tests/scripts/list-enum-consts.pl
+++ b/tests/scripts/list-enum-consts.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 
 use warnings;
 use strict;
diff --git a/tests/scripts/recursion.pl b/tests/scripts/recursion.pl
index 3ad42b1..431e592 100755
--- a/tests/scripts/recursion.pl
+++ b/tests/scripts/recursion.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 
 # Find functions making recursive calls to themselves.
 # (Multiple recursion where a() calls b() which calls a() not covered.)
diff --git a/tests/scripts/run-test-suites.pl b/tests/scripts/run-test-suites.pl
index 5b55fac..d53bedc 100755
--- a/tests/scripts/run-test-suites.pl
+++ b/tests/scripts/run-test-suites.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 
 use warnings;
 use strict;
diff --git a/tests/scripts/test-ref-configs.pl b/tests/scripts/test-ref-configs.pl
index 5b35a60..7d6eb66 100755
--- a/tests/scripts/test-ref-configs.pl
+++ b/tests/scripts/test-ref-configs.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 
 # test standard configurations:
 # - build