Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-134419

systemCaCertificate() openssl backend walks current working directory if system ca directory contains a broken symlink

    XMLWordPrintable

Details

    • Bug
    • Resolution: Unresolved
    • P2: Important
    • None
    • 6.8.2
    • Network: SSL
    • None
    • Ubuntu 20.04 with qt 6.8.2 installed from nixpkgs. x86_64
    • Linux/Other display system

    Description

      If a system CA directory like /etc/ssl/certs contains a broken symlink, then QTlsBackendOpenSSL::systemCaCertificates() will walk all directories in the current working directory, recursively. If the current working directory is the root of a very large tree of directories, the function can block at 100 % CPU for several minutes.

      I think this is due to this line:

      https://github.com/qt/qtbase/blob/6.8.2/src/plugins/tls/openssl/qtlsbackend_openssl.cpp#L398

      my build of qt is optimized but indirect observations suggests that fromPath is called with an empty string. This is coherent with the documentation of canonicalFilePath which returns an empty string in case of error.

       

      Steps to reproduce:

      compile this code:

      #include <QString>
      #include <QSslCertificate>
      int main(char *, char**) {
        QString pem(" \
      -----BEGIN CERTIFICATE----- \
      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 \
      -----END CERTIFICATE----- \
      ");  QByteArray data = pem.toLocal8Bit();
        QSslCertificate cert(data);
        qDebug() << cert.toText();
        return 0;
      }
      

      Ensure that the openssl backend is used, for example by force-linking openssl instead of relying on dlopen().

      The actual certificate here is not important I just wanted it to be valid.

      In a temporary directory create a directory whose name is unique in the full filesystem:

      mkdir df0d6eda-1d57-4612-bf58-e477819dfffc
      

      create a broken symlink as follows:

      sudo ln -s /nonexistent /etc/ssl/certs/ffff.pem

      Ensure that the current working directory is the one containing df0d6eda-1d57-4612-bf58-e477819dfffc and run the program in strace:

      strace -yy -s 1000 ./build/foo |& grep df0d6eda-1d57-4612-bf58-e477819dfffc 
      readlink("/tmp/qtreproducer/df0d6eda-1d57-4612-bf58-e477819dfffc", 0x7ffc31a08010, 1023) = -1 EINVAL (Invalid argument)
      openat(AT_FDCWD, "./df0d6eda-1d57-4612-bf58-e477819dfffc", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 4</tmp/qtreproducer/df0d6eda-1d57-4612-bf58-e477819dfffc>
      fstat(4</tmp/qtreproducer/df0d6eda-1d57-4612-bf58-e477819dfffc>, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
      getdents64(4</tmp/qtreproducer/df0d6eda-1d57-4612-bf58-e477819dfffc>, /* 2 entries */, 32768) = 48
      getdents64(4</tmp/qtreproducer/df0d6eda-1d57-4612-bf58-e477819dfffc>, /* 0 entries */, 32768) = 0
      close(4</tmp/qtreproducer/df0d6eda-1d57-4612-bf58-e477819dfffc>) = 0
      

      Bug: the program attempted to access df0d6eda-1d57-4612-bf58-e477819dfff in the current working directory.

      Expected behavior: the program only attempted to read system directories like /etc/ssl/certificates, but not directories below the current working directory.

       

      Additionally, if the symlink is removed, the issue disappears:

      /tmp/qtreproducer % sudo rm /etc/ssl/certs/ffff.pem
      /tmp/qtreproducer % strace -yy -s 1000 ./build/foo |& grep df0d6eda-1d57-4612-bf58-e477819dfffc
      

      This proves that the broken symlink is a cause of the bug.

       

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            tpochep Timur Pocheptsov
            ggirol-rc ggirol-rc
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:

              Gerrit Reviews

                There are no open Gerrit changes