src/thread/SDL_thread.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 18 Feb 2019 07:50:33 -0800
changeset 12612 07c39cbbeacf
parent 12503 806492103856
permissions -rw-r--r--
Fixed bug 4500 - Heap-Buffer Overflow in Map1toN pertaining to SDL_pixels.c

Petr Pisar

The reproducer has these data in BITMAPINFOHEADER:

biSize = 40
biBitCount = 8
biClrUsed = 131075

SDL_LoadBMP_RW() function passes biBitCount as a color depth to SDL_CreateRGBSurface(), thus 256-color pallete is allocated. But then biClrUsed colors are read from a file and stored into the palette. SDL_LoadBMP_RW should report an error if biClrUsed is greater than 2^biBitCount.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../SDL_internal.h"
    22 
    23 /* System independent thread management routines for SDL */
    24 
    25 #include "SDL_assert.h"
    26 #include "SDL_thread.h"
    27 #include "SDL_thread_c.h"
    28 #include "SDL_systhread.h"
    29 #include "SDL_hints.h"
    30 #include "../SDL_error_c.h"
    31 
    32 
    33 SDL_TLSID
    34 SDL_TLSCreate()
    35 {
    36     static SDL_atomic_t SDL_tls_id;
    37     return SDL_AtomicIncRef(&SDL_tls_id)+1;
    38 }
    39 
    40 void *
    41 SDL_TLSGet(SDL_TLSID id)
    42 {
    43     SDL_TLSData *storage;
    44 
    45     storage = SDL_SYS_GetTLSData();
    46     if (!storage || id == 0 || id > storage->limit) {
    47         return NULL;
    48     }
    49     return storage->array[id-1].data;
    50 }
    51 
    52 int
    53 SDL_TLSSet(SDL_TLSID id, const void *value, void (SDLCALL *destructor)(void *))
    54 {
    55     SDL_TLSData *storage;
    56 
    57     if (id == 0) {
    58         return SDL_InvalidParamError("id");
    59     }
    60 
    61     storage = SDL_SYS_GetTLSData();
    62     if (!storage || (id > storage->limit)) {
    63         unsigned int i, oldlimit, newlimit;
    64 
    65         oldlimit = storage ? storage->limit : 0;
    66         newlimit = (id + TLS_ALLOC_CHUNKSIZE);
    67         storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage)+(newlimit-1)*sizeof(storage->array[0]));
    68         if (!storage) {
    69             return SDL_OutOfMemory();
    70         }
    71         storage->limit = newlimit;
    72         for (i = oldlimit; i < newlimit; ++i) {
    73             storage->array[i].data = NULL;
    74             storage->array[i].destructor = NULL;
    75         }
    76         if (SDL_SYS_SetTLSData(storage) != 0) {
    77             return -1;
    78         }
    79     }
    80 
    81     storage->array[id-1].data = SDL_const_cast(void*, value);
    82     storage->array[id-1].destructor = destructor;
    83     return 0;
    84 }
    85 
    86 static void
    87 SDL_TLSCleanup()
    88 {
    89     SDL_TLSData *storage;
    90 
    91     storage = SDL_SYS_GetTLSData();
    92     if (storage) {
    93         unsigned int i;
    94         for (i = 0; i < storage->limit; ++i) {
    95             if (storage->array[i].destructor) {
    96                 storage->array[i].destructor(storage->array[i].data);
    97             }
    98         }
    99         SDL_SYS_SetTLSData(NULL);
   100         SDL_free(storage);
   101     }
   102 }
   103 
   104 
   105 /* This is a generic implementation of thread-local storage which doesn't
   106    require additional OS support.
   107 
   108    It is not especially efficient and doesn't clean up thread-local storage
   109    as threads exit.  If there is a real OS that doesn't support thread-local
   110    storage this implementation should be improved to be production quality.
   111 */
   112 
   113 typedef struct SDL_TLSEntry {
   114     SDL_threadID thread;
   115     SDL_TLSData *storage;
   116     struct SDL_TLSEntry *next;
   117 } SDL_TLSEntry;
   118 
   119 static SDL_mutex *SDL_generic_TLS_mutex;
   120 static SDL_TLSEntry *SDL_generic_TLS;
   121 
   122 
   123 SDL_TLSData *
   124 SDL_Generic_GetTLSData(void)
   125 {
   126     SDL_threadID thread = SDL_ThreadID();
   127     SDL_TLSEntry *entry;
   128     SDL_TLSData *storage = NULL;
   129 
   130 #if !SDL_THREADS_DISABLED
   131     if (!SDL_generic_TLS_mutex) {
   132         static SDL_SpinLock tls_lock;
   133         SDL_AtomicLock(&tls_lock);
   134         if (!SDL_generic_TLS_mutex) {
   135             SDL_mutex *mutex = SDL_CreateMutex();
   136             SDL_MemoryBarrierRelease();
   137             SDL_generic_TLS_mutex = mutex;
   138             if (!SDL_generic_TLS_mutex) {
   139                 SDL_AtomicUnlock(&tls_lock);
   140                 return NULL;
   141             }
   142         }
   143         SDL_AtomicUnlock(&tls_lock);
   144     }
   145 #endif /* SDL_THREADS_DISABLED */
   146 
   147     SDL_MemoryBarrierAcquire();
   148     SDL_LockMutex(SDL_generic_TLS_mutex);
   149     for (entry = SDL_generic_TLS; entry; entry = entry->next) {
   150         if (entry->thread == thread) {
   151             storage = entry->storage;
   152             break;
   153         }
   154     }
   155 #if !SDL_THREADS_DISABLED
   156     SDL_UnlockMutex(SDL_generic_TLS_mutex);
   157 #endif
   158 
   159     return storage;
   160 }
   161 
   162 int
   163 SDL_Generic_SetTLSData(SDL_TLSData *storage)
   164 {
   165     SDL_threadID thread = SDL_ThreadID();
   166     SDL_TLSEntry *prev, *entry;
   167 
   168     /* SDL_Generic_GetTLSData() is always called first, so we can assume SDL_generic_TLS_mutex */
   169     SDL_LockMutex(SDL_generic_TLS_mutex);
   170     prev = NULL;
   171     for (entry = SDL_generic_TLS; entry; entry = entry->next) {
   172         if (entry->thread == thread) {
   173             if (storage) {
   174                 entry->storage = storage;
   175             } else {
   176                 if (prev) {
   177                     prev->next = entry->next;
   178                 } else {
   179                     SDL_generic_TLS = entry->next;
   180                 }
   181                 SDL_free(entry);
   182             }
   183             break;
   184         }
   185         prev = entry;
   186     }
   187     if (!entry) {
   188         entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
   189         if (entry) {
   190             entry->thread = thread;
   191             entry->storage = storage;
   192             entry->next = SDL_generic_TLS;
   193             SDL_generic_TLS = entry;
   194         }
   195     }
   196     SDL_UnlockMutex(SDL_generic_TLS_mutex);
   197 
   198     if (!entry) {
   199         return SDL_OutOfMemory();
   200     }
   201     return 0;
   202 }
   203 
   204 /* Routine to get the thread-specific error variable */
   205 SDL_error *
   206 SDL_GetErrBuf(void)
   207 {
   208     static SDL_SpinLock tls_lock;
   209     static SDL_bool tls_being_created;
   210     static SDL_TLSID tls_errbuf;
   211     static SDL_error SDL_global_errbuf;
   212     const SDL_error *ALLOCATION_IN_PROGRESS = (SDL_error *)-1;
   213     SDL_error *errbuf;
   214 
   215     /* tls_being_created is there simply to prevent recursion if SDL_TLSCreate() fails.
   216        It also means it's possible for another thread to also use SDL_global_errbuf,
   217        but that's very unlikely and hopefully won't cause issues.
   218      */
   219     if (!tls_errbuf && !tls_being_created) {
   220         SDL_AtomicLock(&tls_lock);
   221         if (!tls_errbuf) {
   222             SDL_TLSID slot;
   223             tls_being_created = SDL_TRUE;
   224             slot = SDL_TLSCreate();
   225             tls_being_created = SDL_FALSE;
   226             SDL_MemoryBarrierRelease();
   227             tls_errbuf = slot;
   228         }
   229         SDL_AtomicUnlock(&tls_lock);
   230     }
   231     if (!tls_errbuf) {
   232         return &SDL_global_errbuf;
   233     }
   234 
   235     SDL_MemoryBarrierAcquire();
   236     errbuf = (SDL_error *)SDL_TLSGet(tls_errbuf);
   237     if (errbuf == ALLOCATION_IN_PROGRESS) {
   238         return &SDL_global_errbuf;
   239     }
   240     if (!errbuf) {
   241         /* Mark that we're in the middle of allocating our buffer */
   242         SDL_TLSSet(tls_errbuf, ALLOCATION_IN_PROGRESS, NULL);
   243         errbuf = (SDL_error *)SDL_malloc(sizeof(*errbuf));
   244         if (!errbuf) {
   245             SDL_TLSSet(tls_errbuf, NULL, NULL);
   246             return &SDL_global_errbuf;
   247         }
   248         SDL_zerop(errbuf);
   249         SDL_TLSSet(tls_errbuf, errbuf, SDL_free);
   250     }
   251     return errbuf;
   252 }
   253 
   254 
   255 /* Arguments and callback to setup and run the user thread function */
   256 typedef struct
   257 {
   258     int (SDLCALL * func) (void *);
   259     void *data;
   260     SDL_Thread *info;
   261     SDL_sem *wait;
   262 } thread_args;
   263 
   264 void
   265 SDL_RunThread(void *data)
   266 {
   267     thread_args *args = (thread_args *) data;
   268     int (SDLCALL * userfunc) (void *) = args->func;
   269     void *userdata = args->data;
   270     SDL_Thread *thread = args->info;
   271     int *statusloc = &thread->status;
   272 
   273     /* Perform any system-dependent setup - this function may not fail */
   274     SDL_SYS_SetupThread(thread->name);
   275 
   276     /* Get the thread id */
   277     thread->threadid = SDL_ThreadID();
   278 
   279     /* Wake up the parent thread */
   280     SDL_SemPost(args->wait);
   281 
   282     /* Run the function */
   283     *statusloc = userfunc(userdata);
   284 
   285     /* Clean up thread-local storage */
   286     SDL_TLSCleanup();
   287 
   288     /* Mark us as ready to be joined (or detached) */
   289     if (!SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_ZOMBIE)) {
   290         /* Clean up if something already detached us. */
   291         if (SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_DETACHED, SDL_THREAD_STATE_CLEANED)) {
   292             if (thread->name) {
   293                 SDL_free(thread->name);
   294             }
   295             SDL_free(thread);
   296         }
   297     }
   298 }
   299 
   300 #ifdef SDL_CreateThread
   301 #undef SDL_CreateThread
   302 #undef SDL_CreateThreadWithStackSize
   303 #endif
   304 #if SDL_DYNAMIC_API
   305 #define SDL_CreateThread SDL_CreateThread_REAL
   306 #define SDL_CreateThreadWithStackSize SDL_CreateThreadWithStackSize_REAL
   307 #endif
   308 
   309 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   310 SDL_Thread *
   311 SDL_CreateThreadWithStackSize(int (SDLCALL * fn) (void *),
   312                  const char *name, const size_t stacksize, void *data,
   313                  pfnSDL_CurrentBeginThread pfnBeginThread,
   314                  pfnSDL_CurrentEndThread pfnEndThread)
   315 #else
   316 SDL_Thread *
   317 SDL_CreateThreadWithStackSize(int (SDLCALL * fn) (void *),
   318                 const char *name, const size_t stacksize, void *data)
   319 #endif
   320 {
   321     SDL_Thread *thread;
   322     thread_args *args;
   323     int ret;
   324 
   325     /* Allocate memory for the thread info structure */
   326     thread = (SDL_Thread *) SDL_malloc(sizeof(*thread));
   327     if (thread == NULL) {
   328         SDL_OutOfMemory();
   329         return (NULL);
   330     }
   331     SDL_zerop(thread);
   332     thread->status = -1;
   333     SDL_AtomicSet(&thread->state, SDL_THREAD_STATE_ALIVE);
   334 
   335     /* Set up the arguments for the thread */
   336     if (name != NULL) {
   337         thread->name = SDL_strdup(name);
   338         if (thread->name == NULL) {
   339             SDL_OutOfMemory();
   340             SDL_free(thread);
   341             return (NULL);
   342         }
   343     }
   344 
   345     /* Set up the arguments for the thread */
   346     args = (thread_args *) SDL_malloc(sizeof(*args));
   347     if (args == NULL) {
   348         SDL_OutOfMemory();
   349         if (thread->name) {
   350             SDL_free(thread->name);
   351         }
   352         SDL_free(thread);
   353         return (NULL);
   354     }
   355     args->func = fn;
   356     args->data = data;
   357     args->info = thread;
   358     args->wait = SDL_CreateSemaphore(0);
   359     if (args->wait == NULL) {
   360         if (thread->name) {
   361             SDL_free(thread->name);
   362         }
   363         SDL_free(thread);
   364         SDL_free(args);
   365         return (NULL);
   366     }
   367 
   368     thread->stacksize = stacksize;
   369 
   370     /* Create the thread and go! */
   371 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   372     ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread);
   373 #else
   374     ret = SDL_SYS_CreateThread(thread, args);
   375 #endif
   376     if (ret >= 0) {
   377         /* Wait for the thread function to use arguments */
   378         SDL_SemWait(args->wait);
   379     } else {
   380         /* Oops, failed.  Gotta free everything */
   381         if (thread->name) {
   382             SDL_free(thread->name);
   383         }
   384         SDL_free(thread);
   385         thread = NULL;
   386     }
   387     SDL_DestroySemaphore(args->wait);
   388     SDL_free(args);
   389 
   390     /* Everything is running now */
   391     return (thread);
   392 }
   393 
   394 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   395 DECLSPEC SDL_Thread *SDLCALL
   396 SDL_CreateThread(int (SDLCALL * fn) (void *),
   397                  const char *name, void *data,
   398                  pfnSDL_CurrentBeginThread pfnBeginThread,
   399                  pfnSDL_CurrentEndThread pfnEndThread)
   400 #else
   401 DECLSPEC SDL_Thread *SDLCALL
   402 SDL_CreateThread(int (SDLCALL * fn) (void *),
   403                  const char *name, void *data)
   404 #endif
   405 {
   406     /* !!! FIXME: in 2.1, just make stackhint part of the usual API. */
   407     const char *stackhint = SDL_GetHint(SDL_HINT_THREAD_STACK_SIZE);
   408     size_t stacksize = 0;
   409 
   410     /* If the SDL_HINT_THREAD_STACK_SIZE exists, use it */
   411     if (stackhint != NULL) {
   412         char *endp = NULL;
   413         const Sint64 hintval = SDL_strtoll(stackhint, &endp, 10);
   414         if ((*stackhint != '\0') && (*endp == '\0')) {  /* a valid number? */
   415             if (hintval > 0) {  /* reject bogus values. */
   416                 stacksize = (size_t) hintval;
   417             }
   418         }
   419     }
   420 
   421 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   422     return SDL_CreateThreadWithStackSize(fn, name, stacksize, data, pfnBeginThread, pfnEndThread);
   423 #else
   424     return SDL_CreateThreadWithStackSize(fn, name, stacksize, data);
   425 #endif
   426 }
   427 
   428 SDL_Thread *
   429 SDL_CreateThreadInternal(int (SDLCALL * fn) (void *), const char *name,
   430                          const size_t stacksize, void *data) {
   431 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   432     return SDL_CreateThreadWithStackSize(fn, name, stacksize, data, NULL, NULL);
   433 #else
   434     return SDL_CreateThreadWithStackSize(fn, name, stacksize, data);
   435 #endif
   436 }
   437 
   438 SDL_threadID
   439 SDL_GetThreadID(SDL_Thread * thread)
   440 {
   441     SDL_threadID id;
   442 
   443     if (thread) {
   444         id = thread->threadid;
   445     } else {
   446         id = SDL_ThreadID();
   447     }
   448     return id;
   449 }
   450 
   451 const char *
   452 SDL_GetThreadName(SDL_Thread * thread)
   453 {
   454     if (thread) {
   455         return thread->name;
   456     } else {
   457         return NULL;
   458     }
   459 }
   460 
   461 int
   462 SDL_SetThreadPriority(SDL_ThreadPriority priority)
   463 {
   464     return SDL_SYS_SetThreadPriority(priority);
   465 }
   466 
   467 void
   468 SDL_WaitThread(SDL_Thread * thread, int *status)
   469 {
   470     if (thread) {
   471         SDL_SYS_WaitThread(thread);
   472         if (status) {
   473             *status = thread->status;
   474         }
   475         if (thread->name) {
   476             SDL_free(thread->name);
   477         }
   478         SDL_free(thread);
   479     }
   480 }
   481 
   482 void
   483 SDL_DetachThread(SDL_Thread * thread)
   484 {
   485     if (!thread) {
   486         return;
   487     }
   488 
   489     /* Grab dibs if the state is alive+joinable. */
   490     if (SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_DETACHED)) {
   491         SDL_SYS_DetachThread(thread);
   492     } else {
   493         /* all other states are pretty final, see where we landed. */
   494         const int thread_state = SDL_AtomicGet(&thread->state);
   495         if ((thread_state == SDL_THREAD_STATE_DETACHED) || (thread_state == SDL_THREAD_STATE_CLEANED)) {
   496             return;  /* already detached (you shouldn't call this twice!) */
   497         } else if (thread_state == SDL_THREAD_STATE_ZOMBIE) {
   498             SDL_WaitThread(thread, NULL);  /* already done, clean it up. */
   499         } else {
   500             SDL_assert(0 && "Unexpected thread state");
   501         }
   502     }
   503 }
   504 
   505 /* vi: set ts=4 sw=4 expandtab: */