JUCE assumes fd can never be 0 (bug?)


#1

In FileOutputStream/FileInputStream and other places it casts a file handle to a void*. It then assumes that this cannot be valid and nullptr, i.e. the original handle from open() cannot be 0. This is not true, 0 is a valid fd.

We have added a hack/workaround to our JUCE copy where we were seeing the problem:

if (fd == 0)
{
  reopen
  fd = newOne
  close(oldOne)
}

Maybe a similar hack should be adopted? I’m sure a more fully formed fix could also be constructed.

Nasty c to show it is possible:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    printf("start num fds\n");
    system("ls /proc/self/fd");

    printf("open fd\n");
    int fd1 = open("/dev/null", O_RDONLY, 00644);
    printf("new fd: %d\n", fd1);

    printf("num fds after open\n");
    system("ls /proc/self/fd");

    close(0);

    printf("num fds after close(0)\n");
    system("ls /proc/self/fd");

    printf("open fd2\n");
    int fd2 = open("/dev/null", O_RDONLY, 00644);
    printf("new fd: %d\n", fd2);

    printf("num fds after open fd2\n");
    system("ls /proc/self/fd");
}

#2

Correct me if I’m wrong here - but I thought this was platform specific behavior where 0 is either invalid or reserved?


#3

@Holy_City I am not sure when 0 is allowed and when it is not, but it is not always disallowed which is the problem here I believe.


#4

While from a C perspective a NULL FILE pointer is technically valid, on most platforms that value is reserved by the system. Like on Unix you can’t read from /dev/null, you can only write to it.

What I’m saying is that it makes sense for JUCE not to treat a file pointer of 0 as a valid file, since it’s not. It’s a special behavior used by some systems, so if you need to use it you should be writing platform specific code.


#5

Here is some JUCE code that reproduces the issue. “After fclose(stdin)” will not be in the output file on Mac or Linux (don’t know about Windows or other).

/*******************************************************************************
 The block below describes the properties of this PIP. A PIP is a short snippet
 of code that can be read by the Projucer and used to generate a JUCE project.

 BEGIN_JUCE_PIP_METADATA

  name:             FDBug
  version:          0.1

  dependencies:     juce_core
  exporters:        xcode_mac

  moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1

  type:             Console

 END_JUCE_PIP_METADATA

*******************************************************************************/

#pragma once


//==============================================================================
int main (int argc, char* argv[])
{
    File temp(File::getSpecialLocation(File::userHomeDirectory).getChildFile("FDBug"));
    temp.deleteFile();
    DBG("Temp file name: " + temp.getFullPathName().quoted());

    {
        ScopedPointer<FileOutputStream> fo = temp.createOutputStream();
        if (!fo->writeString("Before fclose(stdin)\n"))
        {
            DBG("Couldn't write to temp file");
            return 1;
        }
    }

    fclose(stdin);

    {
        ScopedPointer<FileOutputStream> fo = temp.createOutputStream();
        if (!fo->writeString("After fclose(stdin)\n"))
        {
            DBG("Couldn't write to temp file");
            return 1;
        }
    }

    return 0;
}

The checks for if (fileHandle == nullptr) are in juce_posix_SharedCode.h, so that already is platform specific code (just for many platforms).