summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCedric Bail <cedric.bail@samsung.com>2013-11-20 13:02:37 +0900
committerCedric Bail <cedric.bail@samsung.com>2013-11-20 13:02:37 +0900
commit7e8fb93206ee95945bb757267832537c13ab4287 (patch)
tree8ff6ef8d854f28ac0dbcb1ebc86d7df076c03ccf
parent0146e3dacc87189eba100bf5489498290ec9f86b (diff)
eina: fix a possible race condition during eina_file_close.
The lock on the main hash was taken to late (after we took the decision to remove the targeted Eina_File from the cache), this means it was possible to get an Eina_File from the cache that was going to be removed. This patch attempt to fix that potential race condition. Hopefully should fix T461.
-rw-r--r--ChangeLog4
-rw-r--r--NEWS3
-rw-r--r--src/lib/eina/eina_file.c9
-rw-r--r--src/lib/eina/eina_file_common.c15
-rw-r--r--src/lib/eina/eina_file_common.h3
-rw-r--r--src/lib/eina/eina_file_win32.c7
-rw-r--r--src/tests/eina/eina_test_file.c34
7 files changed, 59 insertions, 16 deletions
diff --git a/ChangeLog b/ChangeLog
index 3dc7c2825..6c3dea3a9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2013-11-20 Cedric Bail
+
+ * Eina: Fix a possible race condition during eina_file_close.
+
2013-11-19 Tom Hacohen
* Evas textblock: Fixed order of tags inserted with markup_app/prepend.
diff --git a/NEWS b/NEWS
index 6a1f8bdb0..5ce9bcf17 100644
--- a/NEWS
+++ b/NEWS
@@ -268,7 +268,8 @@ Fixes:
- Fix memory leak in eina_xattr_value_ls.
- Fix magic failure in eina_value_array_count when array has not been allocated.
- Fix issue when wchar_t is signed and eina_unicode does negative array lookups.
- - Eina: fix eina_file_map_lines() to not drop of one character in the last line.
+ - Fix eina_file_map_lines() to not drop of one character in the last line.
+ - Fix a possible race condition during eina_file_close().
* Eet:
- Fix PPC (big endian) image codec bug.
- Fix leak in eet_pbkdf2_sha1 with OpenSSL.
diff --git a/src/lib/eina/eina_file.c b/src/lib/eina/eina_file.c
index 6d7ee56f5..8ae5a0f4e 100644
--- a/src/lib/eina/eina_file.c
+++ b/src/lib/eina/eina_file.c
@@ -305,11 +305,6 @@ eina_file_real_close(Eina_File *file)
{
Eina_File_Map *map;
- if (file->refcount != 0) return;
-
- eina_hash_free(file->rmap);
- eina_hash_free(file->map);
-
EINA_LIST_FREE(file->dead_map, map)
{
munmap(map->map, map->length);
@@ -320,8 +315,6 @@ eina_file_real_close(Eina_File *file)
munmap(file->global_map, file->length);
if (file->fd != -1) close(file->fd);
-
- free(file);
}
static void
@@ -912,6 +905,8 @@ eina_file_open(const char *path, Eina_Bool shared)
n->shared = shared;
eina_lock_new(&n->lock);
eina_hash_direct_add(_eina_file_cache, n->filename, n);
+
+ EINA_MAGIC_SET(n, EINA_FILE_MAGIC);
}
else
{
diff --git a/src/lib/eina/eina_file_common.c b/src/lib/eina/eina_file_common.c
index 7b05b3b24..f5724d70b 100644
--- a/src/lib/eina/eina_file_common.c
+++ b/src/lib/eina/eina_file_common.c
@@ -451,17 +451,26 @@ eina_file_close(Eina_File *file)
EINA_SAFETY_ON_NULL_RETURN(file);
+ eina_lock_take(&_eina_file_lock_cache);
+
eina_lock_take(&file->lock);
file->refcount--;
if (file->refcount == 0) leave = EINA_FALSE;
eina_lock_release(&file->lock);
- if (leave) return;
-
- eina_lock_take(&_eina_file_lock_cache);
+ if (leave) goto end;
eina_hash_del(_eina_file_cache, file->filename, file);
+
+ // Backend specific file resource close
eina_file_real_close(file);
+ // Generic destruction of the file
+ eina_hash_free(file->rmap); file->rmap = NULL;
+ eina_hash_free(file->map); file->map = NULL;
+ EINA_MAGIC_SET(file, 0);
+ free(file);
+
+ end:
eina_lock_release(&_eina_file_lock_cache);
}
diff --git a/src/lib/eina/eina_file_common.h b/src/lib/eina/eina_file_common.h
index 0ac704d02..e3ee0fc55 100644
--- a/src/lib/eina/eina_file_common.h
+++ b/src/lib/eina/eina_file_common.h
@@ -24,11 +24,14 @@
#include "eina_lock.h"
#include "eina_list.h"
+#define EINA_FILE_MAGIC 0xFEEDBEEF
+
typedef struct _Eina_File_Map Eina_File_Map;
typedef struct _Eina_Lines_Iterator Eina_Lines_Iterator;
struct _Eina_File
{
+ EINA_MAGIC;
const char *filename;
Eina_Hash *map;
diff --git a/src/lib/eina/eina_file_win32.c b/src/lib/eina/eina_file_win32.c
index cddf2da81..829050186 100644
--- a/src/lib/eina/eina_file_win32.c
+++ b/src/lib/eina/eina_file_win32.c
@@ -366,9 +366,6 @@ eina_file_real_close(Eina_File *file)
{
Eina_File_Map *map;
- eina_hash_free(file->rmap);
- eina_hash_free(file->map);
-
EINA_LIST_FREE(file->dead_map, map)
{
UnmapViewOfFile(map->map);
@@ -380,8 +377,6 @@ eina_file_real_close(Eina_File *file)
if (file->fm) CloseHandle(file->fm);
if (file->handle) CloseHandle(file->handle);
-
- free(file);
}
static void
@@ -837,6 +832,8 @@ eina_file_open(const char *path, Eina_Bool shared)
n->shared = shared;
eina_lock_new(&n->lock);
eina_hash_direct_add(_eina_file_cache, n->filename, n);
+
+ EINA_MAGIC_SET(n, EINA_FILE_MAGIC);
}
else
{
diff --git a/src/tests/eina/eina_test_file.c b/src/tests/eina/eina_test_file.c
index f2f322550..e8f735d3f 100644
--- a/src/tests/eina/eina_test_file.c
+++ b/src/tests/eina/eina_test_file.c
@@ -441,6 +441,39 @@ START_TEST(eina_test_file_virtualize)
}
END_TEST
+static void *
+_eina_test_file_thread(void *data EINA_UNUSED, Eina_Thread t EINA_UNUSED)
+{
+ Eina_File *f;
+ unsigned int i;
+
+ for (i = 0; i < 10000; ++i)
+ {
+ f = eina_file_open("/bin/sh", EINA_FALSE);
+ fail_if(!f);
+ eina_file_close(f);
+ }
+
+ return NULL;
+}
+
+START_TEST(eina_test_file_thread)
+{
+ Eina_Thread th[4];
+ unsigned int i;
+
+ fail_if(!eina_init());
+
+ for (i = 0; i < 4; i++)
+ fail_if(!(eina_thread_create(&th[i], EINA_THREAD_NORMAL, 0, _eina_test_file_thread, NULL)));
+
+ for (i = 0; i < 4; i++)
+ fail_if(eina_thread_join(th[i]) != NULL);
+
+ eina_shutdown();
+}
+END_TEST
+
void
eina_test_file(TCase *tc)
{
@@ -449,5 +482,6 @@ eina_test_file(TCase *tc)
tcase_add_test(tc, eina_file_ls_simple);
tcase_add_test(tc, eina_file_map_new_test);
tcase_add_test(tc, eina_test_file_virtualize);
+ tcase_add_test(tc, eina_test_file_thread);
}