Use libext2fs to read ext4 params and block bitmap 38/206238/2
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Wed, 15 May 2019 09:44:55 +0000 (11:44 +0200)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Wed, 15 May 2019 10:25:15 +0000 (12:25 +0200)
Until now the ext4 params and the block bitmap was calculated
manually. In some cases it could lead to buffer oveflow. This commit
replaces manual calculations with libext2fs calls.

Change-Id: I54d36a88f1950dd95d0b2a53c3dab605e308250d

fota/CMakeLists.txt
packaging/ode.spec
server/CMakeLists.txt
server/engine/encryption/dmcrypt-engine.cpp
server/ext4-tool.cpp
server/ext4-tool.h
tests/CMakeLists.txt

index e7d41a0..340f9b3 100755 (executable)
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+# Copyright (c) 2017 - 2019 Samsung Electronics Co., Ltd All Rights Reserved
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
 
 SET(PROJECT_NAME "ode-fota")
 
-PKG_CHECK_MODULES(FOTA_DEPS REQUIRED klay-static blkid)
+PKG_CHECK_MODULES(FOTA_DEPS REQUIRED klay-static blkid ext2fs com_err)
 
 SET(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/fota.cpp
                        ${ODE_SERVER}/upgrade-support.cpp
index dde5b9c..fee1d2f 100755 (executable)
@@ -20,6 +20,8 @@ BuildRequires: pkgconfig(libsmack)
 BuildRequires: pkgconfig(blkid)
 BuildRequires: pkgconfig(capi-system-device)
 BuildRequires: pkgconfig(libsystemd)
+BuildRequires: pkgconfig(ext2fs)
+BuildRequires: pkgconfig(com_err)
 Requires: cryptsetup
 
 %global key_storage_plugin_dir %{_libdir}/ode-key-storage-plugin/
index 395027f..9d35440 100644 (file)
@@ -50,6 +50,8 @@ SET(DEPENDENCY        klay
                                libsmack
                                capi-system-device
                                libsystemd
+                               ext2fs
+                               com_err
 )
 
 SET(SERVER_NAME ${PROJECT_NAME}d)
index c65e9db..358dbe7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2016 - 2019 Samsung Electronics Co., Ltd All Rights Reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -329,12 +329,10 @@ bool DMCryptEngine::isMounted()
 
 void DMCryptEngine::encrypt(const BinaryData &key, unsigned int options)
 {
-       // Force filesystem check via fcsf might be able to avoid fail situation during encryption.
-       Ext4Tool ext4Source(source);
-
        for (int retry = 0; retry < 32; retry++) {
                try {
-                       ext4Source.forceCleanUp();
+                       // Force filesystem check via fcsf might be able to avoid fail situation during encryption.
+                       Ext4Tool::forceCleanUp(source);
                } catch (runtime::Exception &e) {
                        continue;
                }
@@ -362,14 +360,12 @@ void DMCryptEngine::decrypt(const BinaryData &key, unsigned int options)
        // should be encrypted here.
        auto cryptoBlkDev = createCryptoBlkDev(source, DM_DEFAULT_LABEL_NAME, sanitizeKey(key), DM_DEFAULT_CRYPTO_NAME);
 
-       // Force filesystem check via fcsf might be able to avoid fail situation during decryption.
-       Ext4Tool ext4CryptoBlkDev(cryptoBlkDev);
-
        start = true;
 
        for (int retry = 0; retry < 32; retry++) {
                try {
-                       ext4CryptoBlkDev.forceCleanUp();
+                       // Force filesystem check via fcsf might be able to avoid fail situation during decryption.
+                       Ext4Tool::forceCleanUp(cryptoBlkDev);
                } catch (runtime::Exception &e) {
                        continue;
                }
index 4dd1780..77d1d3c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2015 - 2019 Samsung Electronics Co., Ltd All Rights Reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
  *  See the License for the specific language governing permissions and
  *  limitations under the License
  */
-#include <fcntl.h>
 
 #include <string>
 #include <vector>
-#include <limits>
 
-#include <klay/filesystem.h>
+extern "C" {
+#include <com_err.h>
+}
+
 #include <klay/process.h>
 #include <klay/exception.h>
 
 
 namespace ode {
 
-#define ODE_SUPERBLOCK_OFFSET          1024
-#define ODE_SUPERBLOCK_SIZE                    1024
-#define ODE_EXT2_MIN_DESC_SIZE         32
-#define ODE_EXT2_MIN_BLOCK_LOG_SIZE    10      /* 1024 */
-#define ODE_EXT2_MIN_BLOCK_SIZE                (1 << ODE_EXT2_MIN_BLOCK_LOG_SIZE)
-
-#define ODE_EXT2_MAGIC                         0xEF53
-
 namespace {
 
-uint32_t divCeilSafely(uint32_t a, uint32_t b)
-{
-       if (!a)
-               return 0;
-
-       if (!b)
-               throw runtime::Exception("Cannot divide by zero");
-
-       return ((a - 1) / b) + 1;
-}
-
 void execAndWait(const std::string &path, std::vector<std::string> &args)
 {
        runtime::Process proc(path, args);
@@ -62,180 +44,47 @@ void execAndWait(const std::string &path, std::vector<std::string> &args)
                throw runtime::Exception(path + " failed for " + args.back());
 }
 
+struct Ext2fsException : public runtime::Exception {
+       Ext2fsException(const std::string& prefixMsg, errcode_t err) :
+               runtime::Exception(prefixMsg + error_message(err))
+       {}
+};
+
 } // namespace
 
-Ext4Tool::Ext4Tool(const std::string &src) :
-       source(src), blockSize(0), totalBlockCount(0)
+Ext4Tool::Ext4Tool(const std::string &src)
 {
-       if (!isExt(src))
-               throw runtime::Exception(source + " is not an ext2/3/4 filesystem");
+       errcode_t ret;
+
+       ret = ext2fs_open(src.c_str(), 0, 0, 0, unix_io_manager, &fs);
+       if (ret != 0)
+               throw Ext2fsException("ext2fs_open() failed: ", ret);
 
-       readInfo();
+       ret = ext2fs_read_block_bitmap(fs);
+       if (ret != 0)
+               throw Ext2fsException("ext2fs_read_block_bitmap() failed: ", ret);
 }
 
 Ext4Tool::~Ext4Tool()
 {
-}
-
-bool Ext4Tool::isExt(const std::string &src)
-{
-       unsigned short magic;
-       runtime::File device(src);
-       bool ret;
-
-       if (!device.exists())
-               throw runtime::Exception(src + " doesn't exist");
-
-       device.open(O_RDONLY);
-
-       device.lseek(ODE_SUPERBLOCK_OFFSET + 56, SEEK_SET);
-       device.read(&magic, 2);
-
-       if (magic == ODE_EXT2_MAGIC)
-               ret = true;
-       else
-               ret = false;
-
-       device.close();
-
-       return ret;
-}
-
-void Ext4Tool::readInfo()
-{
-       // TODO this function should be rewritten
-       uint32_t firstDataBlock = 0;
-       uint32_t blocksPerGroup = 0;
-       uint32_t clustersPerGroup = 0;
-       uint16_t descSize = 0;
-       runtime::File device(source);
-
-       if (!device.exists())
-               throw runtime::Exception(source + " doesn't exist");
-
-       device.open(O_RDONLY);
-
-       // read totalBlockCount
-       device.lseek(ODE_SUPERBLOCK_OFFSET +  4, SEEK_SET);
-       device.read(&totalBlockCount, sizeof(totalBlockCount));
-
-       // read firstDataBlock
-       device.lseek(ODE_SUPERBLOCK_OFFSET + 20, SEEK_SET);
-       device.read(&firstDataBlock, sizeof(firstDataBlock));
-
-       // read blockSize
-       device.lseek(ODE_SUPERBLOCK_OFFSET + 24, SEEK_SET);
-       device.read(&blockSize, sizeof(blockSize));
-       if ((std::numeric_limits<decltype(blockSize)>::max() >> blockSize) < ODE_EXT2_MIN_BLOCK_SIZE)
-               throw runtime::Exception("Block size too big"); // blockSize must be <= 21
-       blockSize = (ODE_EXT2_MIN_BLOCK_SIZE << blockSize);
-
-       // read blocksPerGroup
-       device.lseek(ODE_SUPERBLOCK_OFFSET + 32, SEEK_SET);
-       device.read(&blocksPerGroup, sizeof(blocksPerGroup));
-
-       // read clustersPerGroup
-       device.lseek(ODE_SUPERBLOCK_OFFSET + 36, SEEK_SET);
-       device.read(&clustersPerGroup, sizeof(clustersPerGroup));
-
-       // read descSize
-       device.lseek(ODE_SUPERBLOCK_OFFSET + 0xFE, SEEK_SET);
-       device.read(&descSize, sizeof(descSize));
-       if (descSize < ODE_EXT2_MIN_DESC_SIZE) {
-               descSize = ODE_EXT2_MIN_DESC_SIZE;
-       }
-
-       if (firstDataBlock >= totalBlockCount)
-               throw runtime::Exception("Invalid block info");
-       uint32_t groupDescCount = divCeilSafely(totalBlockCount - firstDataBlock, blocksPerGroup);
-
-       // read group_desc
-       uint32_t descPerBlock = blockSize / descSize;
-       uint32_t descBlockCount = divCeilSafely(groupDescCount, descPerBlock);
-
-       // read first meta block
-       if (descBlockCount > std::numeric_limits<BinaryData::size_type>::max() / blockSize)
-               throw runtime::Exception("Size too big for BinaryData");
-       BinaryData group_desc(descBlockCount * blockSize);
-       // firstDataBlock < totalBlockCount so no overflow is possible
-       device.lseek((firstDataBlock + 1) * blockSize, SEEK_SET);
-       device.read(group_desc.data(), group_desc.size());
-
-       uint32_t blkItr = firstDataBlock;
-
-       // this structure just is used for easy type-casting.
-       struct odeExtGroupDesc {
-               uint32_t blockBitmap;   /* Blocks bitmap block */
-               /* skip other member */
-       };
-
-       // read bitmap
-       {
-               uint32_t start = firstDataBlock;
-               uint32_t real_end = totalBlockCount - 1;
-
-               uint32_t size = (real_end - start) / 8 + 1;
-               size = (size + 7) & ~3;
-               if (std::numeric_limits<size_t>::max() <= size)
-                       throw runtime::Exception("Bitmap size too big");
-
-               bitmap.resize(size);
-       }
-
-       if (groupDescCount >= group_desc.size() / descSize)
-               throw runtime::Exception("Group descriptors buffer too small");
-
-       if (clustersPerGroup > std::numeric_limits<size_t>::max() - 7)
-               throw runtime::Exception("Too many clusters per group");
-
-       if (groupDescCount > (std::numeric_limits<decltype(blkItr)>::max() - blkItr) / clustersPerGroup + 1)
-               throw runtime::Exception("Block iterator may get too big");
-
-       if (((blkItr + clustersPerGroup * (groupDescCount - 1)) >> 3) + ((clustersPerGroup + 7) >> 3) > bitmap.size())
-               throw runtime::Exception("Bitmap size too small");
-
-       for (uint32_t i = 0; i < groupDescCount; i++) {
-               uint32_t blk = reinterpret_cast<odeExtGroupDesc*>(group_desc.data() + i * descSize)->blockBitmap;
-
-               try {
-                       BinaryData block_bitmap(blockSize);
-
-                       if (blk > std::numeric_limits<off_t>::max() / blockSize)
-                               throw runtime::Exception("Block bitmap index too big");
-
-                       device.lseek(blk * blockSize, SEEK_SET);
-                       device.read(block_bitmap.data(), blockSize);
-
-                       memcpy(bitmap.data() + (blkItr >> 3), block_bitmap.data(), (clustersPerGroup + 7) >> 3);
-               } catch (runtime::Exception &e) {
-                       WARN(SINK, "Block " + std::to_string(blk) + " is missing");
-                       memset(bitmap.data() + (blkItr >> 3), 0, (clustersPerGroup + 7) >> 3);
-               }
-               blkItr += clustersPerGroup;
-       }
-
-       device.close();
+       errcode_t err = ext2fs_close_free(&fs);
+       if (err != 0)
+               ERROR(SINK, "ext2fs_close_free() failed: " + std::string(error_message(err)));
 }
 
 uint32_t Ext4Tool::getBlockSize()
 {
-       return blockSize;
+       return fs->blocksize;
 }
 
 uint32_t Ext4Tool::getTotalBlockCount()
 {
-       return totalBlockCount;
+       return fs->super->s_blocks_count;
 }
 
 bool Ext4Tool::isUsedBlock(uint32_t blockIndex)
 {
-       unsigned char *addr = (bitmap.data() + (blockIndex >> 3));
-       unsigned char mask = 1 << (blockIndex & 0x07);
-
-       if (mask & *addr)
-               return true;
-
-       return false;
+       return ext2fs_test_block_bitmap2(fs->block_map, blockIndex);
 }
 
 void Ext4Tool::mkfs(const std::string &src)
@@ -251,14 +100,14 @@ void Ext4Tool::mkfs(const std::string &src)
        execAndWait(mkfsPath, args);
 }
 
-void Ext4Tool::forceCleanUp()
+void Ext4Tool::forceCleanUp(const std::string &src)
 {
        static const char *fsckPath = "/sbin/fsck.ext4";
        std::vector<std::string> args = {
                fsckPath,
                "-f",
                "-y",
-               source
+               src
        };
 
        execAndWait(fsckPath, args);
index 0b0d43c..81cc1fe 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2016 - 2019 Samsung Electronics Co., Ltd All Rights Reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -21,9 +21,7 @@
 
 #include <string>
 
-#include <klay/filesystem.h>
-
-#include "rmi/common.h"
+#include <ext2fs.h>
 
 namespace ode {
 
@@ -40,16 +38,12 @@ public:
        uint32_t getBlockSize();
        uint32_t getTotalBlockCount();
        bool isUsedBlock(uint32_t blockIndex);
-       void forceCleanUp();
 
-       static bool isExt(const std::string &src);
+       static void forceCleanUp(const std::string &src);
        static void mkfs(const std::string &src);
 
 private:
-       void readInfo();
-       std::string source;
-       uint32_t blockSize, totalBlockCount;
-       BinaryData bitmap;
+       ext2_filsys fs;
 };
 
 } // namespace ode
index f803395..7ca1125 100755 (executable)
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+# Copyright (c) 2015 - 2019 Samsung Electronics Co., Ltd All Rights Reserved
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -38,6 +38,8 @@ PKG_CHECK_MODULES(TEST_DEPS REQUIRED  klay
                                                                                gio-2.0
                                                                                libcrypto
                                                                                vconf
+                                                                               ext2fs
+                                                                               com_err
 )
 
 INCLUDE_DIRECTORIES(SYSTEM ${TEST_DEPS_INCLUDE_DIRS} ../server ../)