Compare commits

...

7 Commits

Author SHA1 Message Date
Ryan C. Gordon 9ea364e46e Bumped version to 3.0.1! 2017-10-26 14:38:03 -04:00
Ryan C. Gordon 179bd1d40a Catch access to paths that are just "." or ".." without any path separator.
(transplanted from b6d25a1927c2274cf31166a74b87b24e2752e0e8)
2017-10-26 14:37:16 -04:00
Ryan C. Gordon a80261989e Fixed mounting a symlink to a real directory.
(transplanted from f3459eaad51bbbed4fc2768c0ec65b3005a7f490)
2017-10-26 14:21:36 -04:00
Ryan C. Gordon b8aa7dab87 Fixed some infinite loops that a maliciously-crafted .iso can trigger.
These bugs exposed by American Fuzzy Lop (AFL), a powerful fuzzer.

http://lcamtuf.coredump.cx/afl/
(transplanted from 4f1bf89597e5b76c1c317fbeb2b472481090b4e4)
2017-10-23 14:58:54 -04:00
Ryan C. Gordon b9fd9e8100 Don't allow NULL filenames to be mounted.
Regardless of what the 3.0.0 documentation says, PhysicsFS never handled this
correctly, so now we check for it so you can't get into crashy situations.

Corrected documentation to reflect reality.
(transplanted from 0bbfaf6c5508139ba3d417377c94d75ca921772a)
2017-10-23 12:40:59 -04:00
Ryan C. Gordon e290b8d0a0 Fixed crash when duplicating PHYSFS_Io for zipfiles.
(transplanted from 67ca4c4f043ecf050c395e767845733512c83de2)
2017-10-23 12:16:51 -04:00
Ryan C. Gordon 12b7a80640 Added some notes on API documentation.
(transplanted from 7ee477e62e86838eca158df16a724d417eef125f)
2017-09-27 16:13:00 -04:00
12 changed files with 119 additions and 66 deletions

View File

@ -12,7 +12,7 @@
cmake_minimum_required(VERSION 2.8.4)
project(PhysicsFS)
set(PHYSFS_VERSION 3.0.0)
set(PHYSFS_VERSION 3.0.1)
# Increment this if/when we break backwards compatibility.
set(PHYSFS_SOVERSION 1)

View File

@ -0,0 +1,18 @@
The API documentation is readable in a few ways:
- Read physfs.h; it's _heavily_ documented and the primary source of reference
documentation for the library.
- Run Doxygen over the header, which produces nicer-to-browse documentation in
HTML, LaTeX, manpage, etc formats. This is done for you if Doxygen is
installed and you build the "docs" target in whatever project files CMake
generated for you.
- Too much trouble? We generated the HTML reference for you, online here:
https://icculus.org/physfs/docs/
- We would love well-written tutorials for the latest version of PhysicsFS!
If you write one, we would love to list it here. Drop me a line about it:
icculus@icculus.org ... Thanks!
--ryan.

View File

@ -939,6 +939,10 @@ static int sanitizePlatformIndependentPath(const char *src, char *dst)
while (*src == '/') /* skip initial '/' chars... */
src++;
/* Make sure the entire string isn't "." or ".." */
if ((strcmp(src, ".") == 0) || (strcmp(src, "..") == 0))
BAIL(PHYSFS_ERR_BAD_FILENAME, 0);
prev = dst;
do
{
@ -1012,6 +1016,8 @@ static DirHandle *createDirHandle(PHYSFS_Io *io, const char *newDir,
DirHandle *dirHandle = NULL;
char *tmpmntpnt = NULL;
assert(newDir != NULL); /* should have caught this higher up. */
if (mountPoint != NULL)
{
const size_t len = strlen(mountPoint) + 1;
@ -1025,15 +1031,9 @@ static DirHandle *createDirHandle(PHYSFS_Io *io, const char *newDir,
dirHandle = openDirectory(io, newDir, forWriting);
GOTO_IF_ERRPASS(!dirHandle, badDirHandle);
if (newDir == NULL)
dirHandle->dirName = NULL;
else
{
dirHandle->dirName = (char *) allocator.Malloc(strlen(newDir) + 1);
if (!dirHandle->dirName)
GOTO(PHYSFS_ERR_OUT_OF_MEMORY, badDirHandle);
strcpy(dirHandle->dirName, newDir);
} /* else */
dirHandle->dirName = (char *) allocator.Malloc(strlen(newDir) + 1);
GOTO_IF(!dirHandle->dirName, PHYSFS_ERR_OUT_OF_MEMORY, badDirHandle);
strcpy(dirHandle->dirName, newDir);
if ((mountPoint != NULL) && (*mountPoint != '\0'))
{
@ -1599,7 +1599,7 @@ const char *PHYSFS_getPrefDir(const char *org, const char *app)
assert(*endstr == dirsep);
*endstr = '\0'; /* mask out the final dirsep for now. */
if (!__PHYSFS_platformStat(prefDir, &statbuf))
if (!__PHYSFS_platformStat(prefDir, &statbuf, 1))
{
for (ptr = strchr(prefDir, dirsep); ptr; ptr = strchr(ptr+1, dirsep))
{
@ -1684,21 +1684,20 @@ static int doMount(PHYSFS_Io *io, const char *fname,
DirHandle *prev = NULL;
DirHandle *i;
BAIL_IF(!fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
if (mountPoint == NULL)
mountPoint = "/";
__PHYSFS_platformGrabMutex(stateLock);
if (fname != NULL)
for (i = searchPath; i != NULL; i = i->next)
{
for (i = searchPath; i != NULL; i = i->next)
{
/* already in search path? */
if ((i->dirName != NULL) && (strcmp(fname, i->dirName) == 0))
BAIL_MUTEX_ERRPASS(stateLock, 1);
prev = i;
} /* for */
} /* if */
/* already in search path? */
if ((i->dirName != NULL) && (strcmp(fname, i->dirName) == 0))
BAIL_MUTEX_ERRPASS(stateLock, 1);
prev = i;
} /* for */
dh = createDirHandle(io, fname, mountPoint, 0);
BAIL_IF_MUTEX_ERRPASS(!dh, stateLock, 0);
@ -1725,6 +1724,7 @@ int PHYSFS_mountIo(PHYSFS_Io *io, const char *fname,
const char *mountPoint, int appendToPath)
{
BAIL_IF(!io, PHYSFS_ERR_INVALID_ARGUMENT, 0);
BAIL_IF(!fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
BAIL_IF(io->version != 0, PHYSFS_ERR_UNSUPPORTED, 0);
return doMount(io, fname, mountPoint, appendToPath);
} /* PHYSFS_mountIo */
@ -1738,6 +1738,7 @@ int PHYSFS_mountMemory(const void *buf, PHYSFS_uint64 len, void (*del)(void *),
PHYSFS_Io *io = NULL;
BAIL_IF(!buf, PHYSFS_ERR_INVALID_ARGUMENT, 0);
BAIL_IF(!fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
io = __PHYSFS_createMemoryIo(buf, len, del);
BAIL_IF_ERRPASS(!io, 0);
@ -1760,7 +1761,8 @@ int PHYSFS_mountHandle(PHYSFS_File *file, const char *fname,
int retval = 0;
PHYSFS_Io *io = NULL;
BAIL_IF(file == NULL, PHYSFS_ERR_INVALID_ARGUMENT, 0);
BAIL_IF(!file, PHYSFS_ERR_INVALID_ARGUMENT, 0);
BAIL_IF(!fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
io = __PHYSFS_createHandleIo(file);
BAIL_IF_ERRPASS(!io, 0);
@ -1785,7 +1787,7 @@ int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath)
int PHYSFS_addToSearchPath(const char *newDir, int appendToPath)
{
return doMount(NULL, newDir, NULL, appendToPath);
return PHYSFS_mount(newDir, NULL, appendToPath);
} /* PHYSFS_addToSearchPath */

View File

@ -434,7 +434,7 @@ typedef struct PHYSFS_Version
#ifndef DOXYGEN_SHOULD_IGNORE_THIS
#define PHYSFS_VER_MAJOR 3
#define PHYSFS_VER_MINOR 0
#define PHYSFS_VER_PATCH 0
#define PHYSFS_VER_PATCH 1
#endif /* DOXYGEN_SHOULD_IGNORE_THIS */
@ -2176,11 +2176,15 @@ PHYSFS_DECL int PHYSFS_setAllocator(const PHYSFS_Allocator *allocator);
* or each other, for example.
*
* The mountpoint does not need to exist prior to mounting, which is different
* than those familiar with the Unix concept of "mounting" may not expect.
* than those familiar with the Unix concept of "mounting" may expect.
* As well, more than one archive can be mounted to the same mountpoint, or
* mountpoints and archive contents can overlap...the interpolation mechanism
* still functions as usual.
*
* Specifying a symbolic link to an archive or directory is allowed here,
* regardless of the state of PHYSFS_permitSymbolicLinks(). That function
* only deals with symlinks inside the mounted directory or archive.
*
* \param newDir directory or archive to add to the path, in
* platform-dependent notation.
* \param mountPoint Location in the interpolated tree that this archive
@ -2760,6 +2764,12 @@ PHYSFS_DECL int PHYSFS_enumerate(const char *dir, PHYSFS_EnumerateCallback c,
* This call will fail (and fail to remove from the path) if the element still
* has files open in it.
*
* \warning This function wants the path to the archive or directory that was
* mounted (the same string used for the "newDir" argument of
* PHYSFS_addToSearchPath or any of the mount functions), not the
* path where it is mounted in the tree (the "mountPoint" argument
* to any of the mount functions).
*
* \param oldDir dir/archive to remove.
* \return nonzero on success, zero on failure. Use
* PHYSFS_getLastErrorCode() to obtain the specific error.
@ -3188,7 +3198,7 @@ typedef struct PHYSFS_Io
/**
* \fn int PHYSFS_mountIo(PHYSFS_Io *io, const char *fname, const char *mountPoint, int appendToPath)
* \fn int PHYSFS_mountIo(PHYSFS_Io *io, const char *newDir, const char *mountPoint, int appendToPath)
* \brief Add an archive, built on a PHYSFS_Io, to the search path.
*
* \warning Unless you have some special, low-level need, you should be using
@ -3198,11 +3208,14 @@ typedef struct PHYSFS_Io
* instead of a pathname. Behind the scenes, PHYSFS_mount() calls this
* function with a physical-filesystem-based PHYSFS_Io.
*
* (filename) is only used here to optimize archiver selection (if you name it
* XXXXX.zip, we might try the ZIP archiver first, for example). It doesn't
* need to refer to a real file at all, and can even be NULL. If the filename
* isn't helpful, the system will try every archiver until one works or none
* of them do.
* (newDir) must be a unique string to identify this archive. It is used
* to optimize archiver selection (if you name it XXXXX.zip, we might try
* the ZIP archiver first, for example, or directly choose an archiver that
* can only trust the data is valid by filename extension). It doesn't
* need to refer to a real file at all. If the filename extension isn't
* helpful, the system will try every archiver until one works or none
* of them do. This filename must be unique, as the system won't allow you
* to have two archives with the same name.
*
* (io) must remain until the archive is unmounted. When the archive is
* unmounted, the system will call (io)->destroy(io), which will give you
@ -3211,7 +3224,7 @@ typedef struct PHYSFS_Io
* If this function fails, (io)->destroy(io) is not called.
*
* \param io i/o instance for archive to add to the path.
* \param fname Filename that can represent this stream. Can be NULL.
* \param newDir Filename that can represent this stream.
* \param mountPoint Location in the interpolated tree that this archive
* will be "mounted", in platform-independent notation.
* NULL or "" is equivalent to "/".
@ -3224,12 +3237,12 @@ typedef struct PHYSFS_Io
* \sa PHYSFS_getSearchPath
* \sa PHYSFS_getMountPoint
*/
PHYSFS_DECL int PHYSFS_mountIo(PHYSFS_Io *io, const char *fname,
PHYSFS_DECL int PHYSFS_mountIo(PHYSFS_Io *io, const char *newDir,
const char *mountPoint, int appendToPath);
/**
* \fn int PHYSFS_mountMemory(const void *buf, PHYSFS_uint64 len, void (*del)(void *), const char *fname, const char *mountPoint, int appendToPath)
* \fn int PHYSFS_mountMemory(const void *buf, PHYSFS_uint64 len, void (*del)(void *), const char *newDir, const char *mountPoint, int appendToPath)
* \brief Add an archive, contained in a memory buffer, to the search path.
*
* \warning Unless you have some special, low-level need, you should be using
@ -3239,11 +3252,14 @@ PHYSFS_DECL int PHYSFS_mountIo(PHYSFS_Io *io, const char *fname,
* instead of a pathname. This buffer contains all the data of the archive,
* and is used instead of a real file in the physical filesystem.
*
* (filename) is only used here to optimize archiver selection (if you name it
* XXXXX.zip, we might try the ZIP archiver first, for example). It doesn't
* need to refer to a real file at all, and can even be NULL. If the filename
* isn't helpful, the system will try every archiver until one works or none
* of them do.
* (newDir) must be a unique string to identify this archive. It is used
* to optimize archiver selection (if you name it XXXXX.zip, we might try
* the ZIP archiver first, for example, or directly choose an archiver that
* can only trust the data is valid by filename extension). It doesn't
* need to refer to a real file at all. If the filename extension isn't
* helpful, the system will try every archiver until one works or none
* of them do. This filename must be unique, as the system won't allow you
* to have two archives with the same name.
*
* (ptr) must remain until the archive is unmounted. When the archive is
* unmounted, the system will call (del)(ptr), which will notify you that
@ -3256,7 +3272,7 @@ PHYSFS_DECL int PHYSFS_mountIo(PHYSFS_Io *io, const char *fname,
* \param buf Address of the memory buffer containing the archive data.
* \param len Size of memory buffer, in bytes.
* \param del A callback that triggers upon unmount. Can be NULL.
* \param fname Filename that can represent this stream. Can be NULL.
* \param newDir Filename that can represent this stream.
* \param mountPoint Location in the interpolated tree that this archive
* will be "mounted", in platform-independent notation.
* NULL or "" is equivalent to "/".
@ -3269,12 +3285,12 @@ PHYSFS_DECL int PHYSFS_mountIo(PHYSFS_Io *io, const char *fname,
* \sa PHYSFS_getMountPoint
*/
PHYSFS_DECL int PHYSFS_mountMemory(const void *buf, PHYSFS_uint64 len,
void (*del)(void *), const char *fname,
void (*del)(void *), const char *newDir,
const char *mountPoint, int appendToPath);
/**
* \fn int PHYSFS_mountHandle(PHYSFS_File *file, const char *fname, const char *mountPoint, int appendToPath)
* \fn int PHYSFS_mountHandle(PHYSFS_File *file, const char *newDir, const char *mountPoint, int appendToPath)
* \brief Add an archive, contained in a PHYSFS_File handle, to the search path.
*
* \warning Unless you have some special, low-level need, you should be using
@ -3297,11 +3313,14 @@ PHYSFS_DECL int PHYSFS_mountMemory(const void *buf, PHYSFS_uint64 len,
* but isn't necessarily. The most popular use for this is likely to mount
* archives stored inside other archives.
*
* (filename) is only used here to optimize archiver selection (if you name it
* XXXXX.zip, we might try the ZIP archiver first, for example). It doesn't
* need to refer to a real file at all, and can even be NULL. If the filename
* isn't helpful, the system will try every archiver until one works or none
* of them do.
* (newDir) must be a unique string to identify this archive. It is used
* to optimize archiver selection (if you name it XXXXX.zip, we might try
* the ZIP archiver first, for example, or directly choose an archiver that
* can only trust the data is valid by filename extension). It doesn't
* need to refer to a real file at all. If the filename extension isn't
* helpful, the system will try every archiver until one works or none
* of them do. This filename must be unique, as the system won't allow you
* to have two archives with the same name.
*
* (file) must remain until the archive is unmounted. When the archive is
* unmounted, the system will call PHYSFS_close(file). If you need this
@ -3311,7 +3330,7 @@ PHYSFS_DECL int PHYSFS_mountMemory(const void *buf, PHYSFS_uint64 len,
* If this function fails, PHYSFS_close(file) is not called.
*
* \param file The PHYSFS_File handle containing archive data.
* \param fname Filename that can represent this stream. Can be NULL.
* \param newDir Filename that can represent this stream.
* \param mountPoint Location in the interpolated tree that this archive
* will be "mounted", in platform-independent notation.
* NULL or "" is equivalent to "/".
@ -3323,7 +3342,7 @@ PHYSFS_DECL int PHYSFS_mountMemory(const void *buf, PHYSFS_uint64 len,
* \sa PHYSFS_getSearchPath
* \sa PHYSFS_getMountPoint
*/
PHYSFS_DECL int PHYSFS_mountHandle(PHYSFS_File *file, const char *fname,
PHYSFS_DECL int PHYSFS_mountHandle(PHYSFS_File *file, const char *newDir,
const char *mountPoint, int appendToPath);

View File

@ -49,7 +49,8 @@ static void *DIR_openArchive(PHYSFS_Io *io, const char *name,
const size_t seplen = 1;
assert(io == NULL); /* shouldn't create an Io for these. */
BAIL_IF_ERRPASS(!__PHYSFS_platformStat(name, &st), NULL);
BAIL_IF_ERRPASS(!__PHYSFS_platformStat(name, &st, 1), NULL);
if (st.filetype != PHYSFS_FILETYPE_DIRECTORY)
BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
@ -97,7 +98,7 @@ static PHYSFS_Io *doOpen(void *opaque, const char *name, const int mode)
{
const PHYSFS_ErrorCode err = PHYSFS_getLastErrorCode();
PHYSFS_Stat statbuf;
__PHYSFS_platformStat(f, &statbuf);
__PHYSFS_platformStat(f, &statbuf, 0); /* !!! FIXME: why are we stating here? */
PHYSFS_setErrorCode(err);
} /* if */
@ -164,7 +165,7 @@ static int DIR_stat(void *opaque, const char *name, PHYSFS_Stat *stat)
CVT_TO_DEPENDENT(d, opaque, name);
BAIL_IF_ERRPASS(!d, 0);
retval = __PHYSFS_platformStat(d, stat);
retval = __PHYSFS_platformStat(d, stat, 0);
__PHYSFS_smallFree(d);
return retval;
} /* DIR_stat */

View File

@ -151,18 +151,25 @@ static int iso9660LoadEntries(PHYSFS_Io *io, const int joliet,
/* recordlen = 0 -> no more entries or fill entry */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &recordlen, 1), 0);
if (recordlen == 0)
if (recordlen > 0)
readpos += recordlen; /* ready to seek to next record. */
else
{
PHYSFS_uint64 nextpos;
/* if we are in the last sector of the directory & it's 0 -> end */
if ((dirend - 2048) <= (readpos - 1))
break; /* finished */
/* else skip to the next sector & continue; */
readpos = (((readpos - 1) / 2048) + 1) * 2048;
continue;
} /* if */
nextpos = (((readpos - 1) / 2048) + 1) * 2048;
readpos += recordlen; /* ready to seek to next record. */
/* whoops, can't make forward progress! */
BAIL_IF(nextpos == readpos, PHYSFS_ERR_CORRUPT, 0);
readpos = nextpos;
continue; /* start back at upper loop. */
} /* else */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &extattrlen, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &extent, 4), 0);
@ -203,6 +210,10 @@ static int iso9660LoadEntries(PHYSFS_Io *io, const int joliet,
timestamp = (PHYSFS_sint64) mktime(&t);
extent += extattrlen; /* skip extended attribute record. */
/* infinite loop, corrupt file? */
BAIL_IF((extent * 2048) == dirstart, PHYSFS_ERR_CORRUPT, 0);
if (!iso9660AddEntry(io, joliet, isdir, base, fname, fnamelen,
timestamp, extent * 2048, datalen, unpkarc))
{

View File

@ -455,6 +455,7 @@ static PHYSFS_Io *ZIP_duplicate(PHYSFS_Io *io)
finfo->io = zip_get_io(origfinfo->io, NULL, finfo->entry);
GOTO_IF_ERRPASS(!finfo->io, failed);
initializeZStream(&finfo->stream);
if (finfo->entry->compression_method != COMPMETH_NONE)
{
finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE);

View File

@ -549,11 +549,12 @@ PHYSFS_sint64 __PHYSFS_platformFileLength(void *handle);
*
* This needs to fill in all the fields of (stat). For fields that might not
* mean anything on a platform (access time, perhaps), choose a reasonable
* default.
* default. if (follow), we want to follow symlinks and stat what they
* link to and not the link itself.
*
* Return zero on failure, non-zero on success.
*/
int __PHYSFS_platformStat(const char *fn, PHYSFS_Stat *stat);
int __PHYSFS_platformStat(const char *fn, PHYSFS_Stat *stat, const int follow);
/*
* Flush any pending writes to disk. (opaque) should be cast to whatever data

View File

@ -721,7 +721,7 @@ PHYSFS_sint64 os2TimeToUnixTime(const FDATE *date, const FTIME *time)
} /* os2TimeToUnixTime */
int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *stat)
int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *stat, const int follow)
{
char *cpfname = cvtUtf8ToCodepage(filename);
FILESTATUS3 fs;

View File

@ -296,11 +296,11 @@ int __PHYSFS_platformDelete(const char *path)
} /* __PHYSFS_platformDelete */
int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *st)
int __PHYSFS_platformStat(const char *fname, PHYSFS_Stat *st, const int follow)
{
struct stat statbuf;
BAIL_IF(lstat(filename, &statbuf) == -1, errcodeFromErrno(), 0);
const int rc = follow ? stat(fname, &statbuf) : lstat(fname, &statbuf);
BAIL_IF(rc == -1, errcodeFromErrno(), 0);
if (S_ISREG(statbuf.st_mode))
{
@ -330,7 +330,7 @@ int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *st)
st->createtime = statbuf.st_ctime;
st->accesstime = statbuf.st_atime;
st->readonly = (access(filename, W_OK) == -1);
st->readonly = (access(fname, W_OK) == -1);
return 1;
} /* __PHYSFS_platformStat */

View File

@ -960,7 +960,7 @@ static int isSymlink(const WCHAR *wpath, const DWORD attr)
} /* isSymlink */
int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *st)
int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *st, const int follow)
{
WIN32_FILE_ATTRIBUTE_DATA winstat;
WCHAR *wstr = NULL;
@ -975,7 +975,7 @@ int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *st)
if (!rc)
err = GetLastError();
else /* check for symlink while wstr is still available */
issymlink = isSymlink(wstr, winstat.dwFileAttributes);
issymlink = !follow && isSymlink(wstr, winstat.dwFileAttributes);
__PHYSFS_smallFree(wstr);
BAIL_IF(!rc, errcodeFromWinApiError(err), 0);

View File

@ -32,7 +32,7 @@
#define TEST_VERSION_MAJOR 3
#define TEST_VERSION_MINOR 0
#define TEST_VERSION_PATCH 0
#define TEST_VERSION_PATCH 1
static FILE *history_file = NULL;
static PHYSFS_uint32 do_buffer_size = 0;