From c7c24f841322528c17714482d150b769d0afcddb Mon Sep 17 00:00:00 2001
From: imarkov <imarkov@tarantool.org>
Date: Thu, 22 Feb 2018 11:55:43 +0300
Subject: [PATCH] fio: Fix race condition in fio.read

When fio.read  from multiple fibers is performed, as fio.read yields before
performing actual read operation and ibuf shared among several fibers
may be corrupted and read data is mixed.

The fix is to create new ibuf for each fio.read call in case
buffer is not specified.

Closes #3187
---
 src/lua/fio.lua       |  3 +-
 test/app/fio.result   | 70 +++++++++++++++++++++++++++++++++++++++++++
 test/app/fio.test.lua | 29 ++++++++++++++++++
 3 files changed, 100 insertions(+), 2 deletions(-)

diff --git a/src/lua/fio.lua b/src/lua/fio.lua
index d48c709b15..73719df90e 100644
--- a/src/lua/fio.lua
+++ b/src/lua/fio.lua
@@ -40,8 +40,7 @@ fio_methods.read = function(self, buf, size)
     end
     if not ffi.istype(const_char_ptr_t, buf) then
         size = buf or size
-        tmpbuf = buffer.IBUF_SHARED
-        tmpbuf:reset()
+        tmpbuf = buffer.ibuf()
         buf = tmpbuf:reserve(size)
     end
     local res, err = internal.read(self.fh, buf, size)
diff --git a/test/app/fio.result b/test/app/fio.result
index 0fc8582530..6c4609855f 100644
--- a/test/app/fio.result
+++ b/test/app/fio.result
@@ -1067,6 +1067,76 @@ fio.unlink(tmpfile)
 ---
 - true
 ...
+tmp1 = fio.pathjoin(tmpdir, "tmp1")
+---
+...
+tmp2= fio.pathjoin(tmpdir, "tmp2")
+---
+...
+test_run:cmd("setopt delimiter ';'")
+---
+- true
+...
+function write_file(name, odd)
+    local fh = fio.open(name, { 'O_RDWR', 'O_TRUNC', 'O_CREAT' }, 0777)
+    if odd then
+        fh:write(string.rep('1', 100))
+    else
+        fh:write(string.rep('2', 100))
+    end
+    fh:write(name)
+    fh:seek(0)
+    return fh
+end;
+---
+...
+test_run:cmd("setopt delimiter ''");
+---
+- true
+...
+fh1 = write_file(tmp1)
+---
+...
+fh2 = write_file(tmp2)
+---
+...
+fiber = require('fiber')
+---
+...
+digest = require('digest')
+---
+...
+str = fh1:read()
+---
+...
+fh1:seek(0)
+---
+- 0
+...
+hash = digest.crc32(str)
+---
+...
+ch = fiber.channel(1)
+---
+...
+f1 = fiber.create(function() str = fh1:read() ch:put(digest.crc32(str)) end)
+---
+...
+f2 = fiber.create(function() str = fh2:read() end)
+---
+...
+ch:get() == hash
+---
+- true
+...
+fio.unlink(tmp1)
+---
+- true
+...
+fio.unlink(tmp2)
+---
+- true
+...
 fio.rmdir(tmpdir)
 ---
 - true
diff --git a/test/app/fio.test.lua b/test/app/fio.test.lua
index cd0bc16c65..0850413d9b 100644
--- a/test/app/fio.test.lua
+++ b/test/app/fio.test.lua
@@ -342,4 +342,33 @@ fio.path.lexists(link)
 
 fio.unlink(link)
 fio.unlink(tmpfile)
+tmp1 = fio.pathjoin(tmpdir, "tmp1")
+tmp2= fio.pathjoin(tmpdir, "tmp2")
+test_run:cmd("setopt delimiter ';'")
+function write_file(name, odd)
+    local fh = fio.open(name, { 'O_RDWR', 'O_TRUNC', 'O_CREAT' }, 0777)
+    if odd then
+        fh:write(string.rep('1', 100))
+    else
+        fh:write(string.rep('2', 100))
+    end
+    fh:write(name)
+    fh:seek(0)
+    return fh
+end;
+test_run:cmd("setopt delimiter ''");
+fh1 = write_file(tmp1)
+fh2 = write_file(tmp2)
+fiber = require('fiber')
+digest = require('digest')
+str = fh1:read()
+fh1:seek(0)
+hash = digest.crc32(str)
+ch = fiber.channel(1)
+f1 = fiber.create(function() str = fh1:read() ch:put(digest.crc32(str)) end)
+f2 = fiber.create(function() str = fh2:read() end)
+ch:get() == hash
+
+fio.unlink(tmp1)
+fio.unlink(tmp2)
 fio.rmdir(tmpdir)
-- 
GitLab