Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create temporary files and folders #10966

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1304,6 +1304,9 @@ if(ANDROID)
sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
set(HAVE_SDL_FSOPS TRUE)

sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_posix_tmpfs.c")
set(HAVE_SDL_TMPFS TRUE)

if(SDL_HAPTIC)
set(SDL_HAPTIC_ANDROID 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/haptic/android/*.c")
Expand Down Expand Up @@ -1480,6 +1483,9 @@ elseif(EMSCRIPTEN)
sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
set(HAVE_SDL_FSOPS TRUE)

sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_posix_tmpfs.c")
set(HAVE_SDL_TMPFS TRUE)

if(SDL_CAMERA)
set(SDL_CAMERA_DRIVER_EMSCRIPTEN 1)
set(HAVE_CAMERA TRUE)
Expand Down Expand Up @@ -1827,6 +1833,9 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
set(HAVE_SDL_FSOPS TRUE)

sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_posix_tmpfs.c")
set(HAVE_SDL_TMPFS TRUE)

set(SDL_TIME_UNIX 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c")
set(HAVE_SDL_TIME TRUE)
Expand Down Expand Up @@ -2025,6 +2034,7 @@ elseif(WINDOWS)
set(SDL_FILESYSTEM_WINDOWS 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/windows/*.c")
set(HAVE_SDL_FILESYSTEM TRUE)
set(HAVE_SDL_TMPFS TRUE) # SDL_tmpfs is included in the windows sources

set(SDL_FSOPS_WINDOWS 1)
set(HAVE_SDL_FSOPS TRUE)
Expand Down Expand Up @@ -2274,6 +2284,9 @@ elseif(APPLE)
sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
set(HAVE_SDL_FSOPS TRUE)

sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_posix_tmpfs.c")
set(HAVE_SDL_TMPFS TRUE)

if(SDL_SENSOR)
if(IOS OR VISIONOS OR WATCHOS)
set(SDL_SENSOR_COREMOTION 1)
Expand Down Expand Up @@ -2477,6 +2490,9 @@ elseif(HAIKU)
sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
set(HAVE_SDL_FSOPS TRUE)

sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_posix_tmpfs.c")
set(HAVE_SDL_TMPFS TRUE)

set(SDL_TIME_UNIX 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c")
set(HAVE_SDL_TIME TRUE)
Expand Down Expand Up @@ -2517,6 +2533,9 @@ elseif(RISCOS)
sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
set(HAVE_SDL_FSOPS TRUE)

sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_posix_tmpfs.c")
set(HAVE_SDL_TMPFS TRUE)

set(SDL_TIME_UNIX 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c")
set(HAVE_SDL_TIME TRUE)
Expand Down Expand Up @@ -3043,6 +3062,10 @@ if(NOT HAVE_SDL_FSOPS)
set(SDL_FSOPS_DUMMY 1)
sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/dummy/SDL_sysfsops.c")
endif()
if (NOT HAVE_SDL_TMPFS)
set(SDL_TMPFS_DUMMY 1)
sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/dummy/SDL_dummy_tmpfs.c")
endif()
if(NOT HAVE_SDL_LOCALE)
set(SDL_LOCALE_DUMMY 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/dummy/*.c")
Expand Down
82 changes: 79 additions & 3 deletions include/SDL3/SDL_filesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_iostream.h>

#include <SDL3/SDL_begin_code.h>

Expand Down Expand Up @@ -137,9 +138,10 @@ extern SDL_DECLSPEC char * SDLCALL SDL_GetPrefPath(const char *org, const char *
/**
* The type of the OS-provided default folder for a specific purpose.
*
* Note that the Trash folder isn't included here, because trashing files
* usually involves extra OS-specific functionality to remember the file's
* original location.
* Note that many common folders, like the Trash, the Temp folder or
* app-specific folders like AppData are not listed here; using them properly
* requires more treatment than fetching the folder path and using it. To use
* these folders, see their dedicated functions.
*
* The folders supported per platform are:
*
Expand Down Expand Up @@ -465,6 +467,80 @@ extern SDL_DECLSPEC char ** SDLCALL SDL_GlobDirectory(const char *path, const ch
*/
extern SDL_DECLSPEC char * SDLCALL SDL_GetCurrentDirectory(void);

/**
* Create a secure temporary file.
*
* This function is not path-based to avoid race conditions. Returning a path
* and letting the caller create the file opens a time-of-check-to-time-of-use
* (TOCTOU) safety issue, where an attacker can use the small delay between the
* moment the name is generated and the moment the file is created to create
* the file first and give it undesirable attributes, such as giving itself
* full read/write access to the file, or making the file a symlink to another,
* sensitive file.
*
* \returns an open IOStream object to the file, or NULL on error; call
* SDL_GetError() for details.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_CreateUnsafeTempFile
* \sa SDL_CreateTempFolder
*/
extern SDL_DECLSPEC SDL_IOStream *SDLCALL SDL_CreateSafeTempFile(void);

/**
* Create a temporary file, with less security considerations.
*
* Unlike SDL_CreateSafeTempFile(), this function provides a path, which can
* then be used like any other file on the filesystem. This has security
* implications; an attacker could exploit the small delay between the moment
* the name is generated and the moment the file is created to create the file
* first and give it undesirable attributes, such as giving itself full
* read/write access to the file, or making the file a symlink to another,
* sensitive file.
Comment on lines +494 to +502
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this function, as documented here, is too dangerous to be useful: the author of a SDL game cannot know whether their game will be run on a multi-user system where another user is attempting to carry out a symlink attack.

Instead, I would suggest modelling it on Python's NamedTemporaryFile, which returns (an object containing) a file handle and a path:

This function operates exactly as TemporaryFile() does, except that the file is guaranteed to have a visible name in the file system (on Unix, the directory entry is not unlinked). That name can be retrieved from the name attribute of the returned file-like object.

For example a good signature might be: SDL_IOStream *SDL_CreateNamedTempFile(char **name_out) (and perhaps SDL_IOStream *SDL_CreateTempFile(void) could just be implemented as syntactic sugar for SDL_CreateNamedTempFile(NULL)).

https://docs.gtk.org/glib/func.mkstemp.html is another implementation of the same idea.

It would be a good idea to document what you can safely do with the temporary file by its path (read it, edit it in-place, or overwrite it), and what you cannot safely do with it (after you delete it, even if it's only for a moment, it is no longer safe to reuse that path because an attacker could have hijacked it).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The moment paths are involved, a temporary file is no longer "safe" in at least one way, because other processes can refer to it and at least read it/write to it (I believe no environment enforce file locks? Linux provide locks, but they require voluntary implementation). I'm worried about developers thinking that the function is merely a more convenient path-based alternative to the current SDL_CreateSafeTempFile, rather than an insecure alternative to be used by APIs that are inherently insecure, like AppIndicator.

I would prefer to stick to the "safe"/"unsafe" naming to make sure this is properly conveyed to people who eagerly copy-paste code without reading the documentation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an insecure alternative to be used by APIs that are inherently insecure

What threat model do you have in mind here - what are you assuming that the attacker can and can't do? (It isn't ever really possible for a component to be "secure" in the abstract, but it can be secure against a specific threat model.)

a temporary file is no longer "safe" in at least one way, because other processes can refer to it and at least read it/write to it

Other processes that are running as the same user can do that, yes; but in the typical Unix security model, there is already no security boundary between processes that are running as the same user. They can ptrace() or kill() each other; they are reading and trusting the same configuration files in $HOME and state in $XDG_RUNTIME_DIR; IPC services like X11, Wayland and D-Bus treat them as essentially equivalent; and so on. The threat model that Unix is/was designed for is that you trust yourself and root, but other users are assumed to be hostile.

Other users can be prevented from reading and writing files by setting appropriate permissions on those files, so if your threat model is that the attacker is running code as a different user ID, "all" we need to do is to avoid time-of-check/time-of-use attacks. Once the file is in place and has our desired permissions, other users can't mess with it - unless we delete it, in which case we no longer "own" that path and all bets are off.

If you have a different threat model in mind, then please describe it, and we should evaluate these APIs against that threat model as well, but it's entirely possible that the answer will be: if that is your threat model, then you have already lost, because the OS was not designed for it.

Copy link
Contributor

@smcv smcv Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe no environment enforce file locks? Linux provide locks, but they require voluntary implementation

Yes, mandatory locking isn't implemented on Unix, and the advisory locking that does exist is a data-integrity mechanism rather than a security mechanism (it doesn't prevent an adversary from attacking you, all it does is to let two cooperating processes agree on how to avoid data loss).

Linux used to have mandatory locking on some filesystems, but it was removed, because it was considered to be a denial-of-service security vulnerability (a malicious user with read access could prevent another user from accessing files).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[...] in the typical Unix security model, there is already no security boundary between processes that are running as the same user.

Actually, on Linux at least, there are some kernel settings that can provide better protection. For example, the YAMA module provides /proc/sys/kernel/yama/ptrace_scope, which allows different levels of restrictions on which process can ptrace which other. It provides 4 levels of security (0 = any process with the same uid, 1 = only parent processes, 2 = only processes with CAP_SYS_PTRACE, 3 = deactivated entirely). On my Ubuntu 24.04, it defaults to a value of 1.

The linked documentation states, towards the beginning, some of the issues with processes having the ability to read the memory of other processes with the same uid. That's the threat I'd like to avoid: leaking potentially sensitive information to other processes. ptrace seems to be mitigated to a certain degree on modern systems, and kill wouldn't leak information from temporary files created with tmpfile(), which deletes on close. Other possible attacks have their own mitigation, though all those I can think of is within the control of the OS, the environment, the app developer, the end user, or an entity other than SDL.

I understand SDL can't change how operating systems work, but operating systems may provide settings to adjust how they work. However, they can't reasonably provide settings to change how SDL works, so if we choose to ignore the problem and leave the issue there, there isn't much that developers and end users can do about it, short of patching SDL manually. I'd rather have SDL not be the cause of the issue, even if some OSes will hold some issues out of SDL's control.

*
* The path string is owned by the caller and must be freed with SDL_free().
*
* \returns an absolute path to the temporary file, or NULL on error; call
* SDL_GetError() for details. If a path is returned, it is encoded in
* OS-specific format, and is guaranteed to finish with a path
* separator.
Comment on lines +508 to +509
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filenames don't end with a path separator?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy-pasting bug, my bad :)

*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_CreateSafeTempFile
* \sa SDL_CreateTempFolder
*/
extern SDL_DECLSPEC char *SDLCALL SDL_CreateUnsafeTempFile(void);

/**
* Create a temporary folder.
*
* Keep in mind any program running as the same user as your program can access
* the contents of the folders and the files in it. Do not perform sensitive
* tasks using the temporary folder. If you need one or more temporary files
* for sensitive purposes, use SDL_CreateSafeTempFile().
*
* The path string is owned by the caller and must be freed with SDL_free().
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When in doubt, I think this is the one to recommend. That way, it doesn't matter whether the caller uses atomic or non-atomic I/O inside the temporary directory. Typical stdio-like functions like SDL_SaveBMP() are non-atomic, because it's difficult to do anything other than that on Windows.

I'd suggest calling it a directory rather than a folder, consistent with SDL_GlobDirectory. On Windows, there is a subtle distinction between directories (things that genuinely exist in the filesystem) and folders (a UI construct that behaves like a directory but might be virtual), and this one is specifically a directory. On Unix, folders are not a concept that really exists at all, except perhaps in UIs and user-facing documentation: there are only directories.

any program running as the same user as your program can access the contents of the folders and the files in it

This is equally true for anything that you write to the filesystem, temporary or not!

On Unix, mkdtemp() creates the directory with permissions 0700 (private to the user), so other users cannot interfere with its contents. A typical Unix security model is that all programs running as the same user ID are equally-privileged (they can trace each other, modify files that they trust, and so on) so there is no defending against attacks coming from the same user ID anyway, unless the programs are sandboxed (like Flatpak) in which case they should be using a separate /tmp. The interesting security boundary is that it's necessary to defend against attacks from a different user ID - either a second human user of a multi-user machine, or a privilege-separated system user like avahi, daemon or www-data.

I don't know what the security properties are on Windows, but I'm pretty sure any attempt to defend against attacks coming from a non-sandboxed program running as the same logged-in user would just be security theatre, similar to the situation on Unix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any program running as the same user as your program can access the contents of the folders and the files in it

This is equally true for anything that you write to the filesystem, temporary or not!

Actually, tmpfile() on Unix is specifically designed to help against this. The file does not actually exist on the filesystem; the file descriptor is not linked anywhere on the filesystem, and cannot even be identified by a different process. That's what SDL_CreateSafeTempFile() uses under the hood on Unix.

Hopefully, the name "Unsafe" sounds scary enough to encourage someone to at least take a look at the documentation :)

*
* \returns an absolute path to the temporary folder, or NULL on error; call
* SDL_GetError() for details. If a path is returned, it is encoded in
* OS-specific format, and is guaranteed to finish with a path
* separator.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_CreateSafeTempFile
* \sa SDL_CreateUnsafeTempFile
*/
extern SDL_DECLSPEC char *SDLCALL SDL_CreateTempFolder(void);

/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
Expand Down
3 changes: 3 additions & 0 deletions src/dynapi/SDL_dynapi.sym
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,9 @@ SDL3_0.0.0 {
SDL_SetGPUAllowedFramesInFlight;
SDL_RenderTextureAffine;
SDL_WaitAndAcquireGPUSwapchainTexture;
SDL_CreateSafeTempFile;
SDL_CreateUnsafeTempFile;
SDL_CreateTempFolder;
# extra symbols go here (don't modify this line)
local: *;
};
3 changes: 3 additions & 0 deletions src/dynapi/SDL_dynapi_overrides.h
Original file line number Diff line number Diff line change
Expand Up @@ -1232,3 +1232,6 @@
#define SDL_SetGPUAllowedFramesInFlight SDL_SetGPUAllowedFramesInFlight_REAL
#define SDL_RenderTextureAffine SDL_RenderTextureAffine_REAL
#define SDL_WaitAndAcquireGPUSwapchainTexture SDL_WaitAndAcquireGPUSwapchainTexture_REAL
#define SDL_CreateSafeTempFile SDL_CreateSafeTempFile_REAL
#define SDL_CreateUnsafeTempFile SDL_CreateUnsafeTempFile_REAL
#define SDL_CreateTempFolder SDL_CreateTempFolder_REAL
3 changes: 3 additions & 0 deletions src/dynapi/SDL_dynapi_procs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1238,3 +1238,6 @@ SDL_DYNAPI_PROC(bool,SDL_RunOnMainThread,(SDL_MainThreadCallback a,void *b,bool
SDL_DYNAPI_PROC(bool,SDL_SetGPUAllowedFramesInFlight,(SDL_GPUDevice *a,Uint32 b),(a,b),return)
SDL_DYNAPI_PROC(bool,SDL_RenderTextureAffine,(SDL_Renderer *a,SDL_Texture *b,const SDL_FRect *c,const SDL_FPoint *d,const SDL_FPoint *e,const SDL_FPoint *f),(a,b,c,d,e,f),return)
SDL_DYNAPI_PROC(bool,SDL_WaitAndAcquireGPUSwapchainTexture,(SDL_GPUCommandBuffer *a,SDL_Window *b,SDL_GPUTexture **c,Uint32 *d,Uint32 *e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(SDL_IOStream*,SDL_CreateSafeTempFile,(void),(),return)
SDL_DYNAPI_PROC(char*,SDL_CreateUnsafeTempFile,(void),(),return)
SDL_DYNAPI_PROC(char*,SDL_CreateTempFolder,(void),(),return)
15 changes: 15 additions & 0 deletions src/filesystem/SDL_filesystem.c
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,21 @@ char *SDL_GetCurrentDirectory(void)
return SDL_SYS_GetCurrentDirectory();
}

SDL_IOStream *SDL_CreateSafeTempFile(void)
{
return SDL_SYS_CreateSafeTempFile();
}

char *SDL_CreateUnsafeTempFile(void)
{
return SDL_SYS_CreateUnsafeTempFile();
}

char *SDL_CreateTempFolder(void)
{
return SDL_SYS_CreateTempFolder();
}

void SDL_InitFilesystem(void)
{
}
Expand Down
4 changes: 4 additions & 0 deletions src/filesystem/SDL_sysfilesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,9 @@ typedef bool (*SDL_GlobEnumeratorFunc)(const char *path, SDL_EnumerateDirectoryC
typedef bool (*SDL_GlobGetPathInfoFunc)(const char *path, SDL_PathInfo *info, void *userdata);
extern char **SDL_InternalGlobDirectory(const char *path, const char *pattern, SDL_GlobFlags flags, int *count, SDL_GlobEnumeratorFunc enumerator, SDL_GlobGetPathInfoFunc getpathinfo, void *userdata);

extern SDL_IOStream *SDL_SYS_CreateSafeTempFile(void);
extern char *SDL_SYS_CreateUnsafeTempFile(void);
extern char *SDL_SYS_CreateTempFolder(void);

#endif

41 changes: 41 additions & 0 deletions src/filesystem/dummy/SDL_dummy_tmpfs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <[email protected]>

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/

#include "SDL_internal.h"
#include "../SDL_sysfilesystem.h"

SDL_IOStream *SDL_SYS_CreateSafeTempFile(void)
{
SDL_Unsupported();
return NULL;
}

char *SDL_SYS_CreateUnsafeTempFile(void)
{
SDL_Unsupported();
return NULL;
}

char *SDL_SYS_CreateTempFolder(void)
{
SDL_Unsupported();
return NULL;
}
90 changes: 90 additions & 0 deletions src/filesystem/posix/SDL_posix_tmpfs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <[email protected]>

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/

#include "SDL_internal.h"
#include "../SDL_sysfilesystem.h"
#include "../../file/SDL_iostream_c.h"

#include <unistd.h>
#include <string.h>
#include <errno.h>

SDL_IOStream *SDL_SYS_CreateSafeTempFile(void)
{
FILE *file = tmpfile();

if (!file) {
SDL_SetError("Could not tmpfile(): %s", strerror(errno));
return NULL;
}

return SDL_IOFromFP(file, true);
}

char *SDL_SYS_CreateUnsafeTempFile(void)
{
/* TODO: Check for possible alternatives to /tmp, like $TMP */
char template[] = "/tmp/tmp.XXXXXX";
Comment on lines +44 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's conventional to try $TMPDIR before /tmp, but not usually $TMP.

The template can be static const char template[] if you're going to strdup it anyway.


char *file = SDL_strdup(template);

if (!file) {
return NULL;
}

int fd = mkstemp(file);

if (fd < 0) {
SDL_free(file);
SDL_SetError("Could not mkstemp(): %s", strerror(errno));
return NULL;
}

/* Normal usage of mkstemp() would use the file descriptor rather than the
path, to avoid issues. In this function, security is assumed to be
unimportant, so no need to worry about it. */
Comment on lines +62 to +63
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, on a multi-user system, security is never unimportant: even if the data you're saving is unimportant, symlink attacks like the one described in #11887 can be used to destroy unrelated data owned by the same user.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've called the function "unsafe" to convey that :) This function is meant for interoperability with software that require a path and accept nothing else, for example AppIndicator.

Normally, the symlink attack is mitigated within the function by mkstemp and outside the function by the developers being careful with their code - surely the temp path isn't the only place where an arbitrary filename can be written to. If it can be relevant, I can write that in the documentation.

/* See https://stackoverflow.com/questions/27680807/mkstemp-is-it-safe-to-close-descriptor-and-reopen-it-again */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tl;dr: "no,it is not safe".

close(fd);

return file;
}

char *SDL_SYS_CreateTempFolder(void)
{
/* TODO: Check for possible alternatives to /tmp, like $TMP */
char template[] = "/tmp/tmp.XXXXXX";
Comment on lines +72 to +73
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments as for the function that uses mkstemp()


char *folder = SDL_strdup(template);

if (!folder) {
return NULL;
}

char *res = mkdtemp(folder);

if (!res) {
SDL_free(folder);
SDL_SetError("Could not mkdtemp(): %s", strerror(errno));
return NULL;
}

return folder;
}
2 changes: 2 additions & 0 deletions src/filesystem/unix/SDL_sysfilesystem.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
// System dependent filesystem routines

#include "../SDL_sysfilesystem.h"
#include "../../file/SDL_iostream_c.h"

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
Expand Down
Loading
Loading