-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add test for loading corrupted metadata
Signed-off-by: Katarzyna Treder <katarzyna.treder@h-partners.com>
- Loading branch information
Katarzyna Treder
committed
Sep 13, 2024
1 parent
8f5dbc9
commit 7de680f
Showing
1 changed file
with
149 additions
and
0 deletions.
There are no files selected for viewing
149 changes: 149 additions & 0 deletions
149
test/functional/tests/security/test_load_corrupted_metadata.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
# | ||
# Copyright(c) 2021 Intel Corporation | ||
# Copyright(c) 2023-2024 Huawei Technologies Co., Ltd. | ||
# SPDX-License-Identifier: BSD-3-Clause | ||
# | ||
|
||
import pytest | ||
import random | ||
|
||
from api.cas import casadm | ||
from api.cas.cache_config import ( | ||
CacheMode, | ||
CacheLineSize, | ||
CleaningPolicy, | ||
PromotionPolicy, | ||
SeqCutOffPolicy, | ||
) | ||
from storage_devices.device import Device | ||
from core.test_run import TestRun | ||
from test_tools.dd import Dd | ||
from test_tools.fs_utils import remove | ||
from test_utils.filesystem.symlink import Symlink | ||
from test_utils.os_utils import load_kernel_module, unload_kernel_module, is_kernel_module_loaded | ||
from test_utils.output import CmdException | ||
from test_utils.size import Size, Unit | ||
|
||
|
||
@pytest.mark.parametrizex("cache_mode", CacheMode) | ||
@pytest.mark.parametrizex("cache_line_size", CacheLineSize) | ||
@pytest.mark.parametrizex("cleaning_policy", CleaningPolicy) | ||
@pytest.mark.parametrizex("promotion_policy", PromotionPolicy) | ||
@pytest.mark.parametrizex("seq_cutoff_policy", SeqCutOffPolicy) | ||
def test_load_corrupted_metadata(cache_mode, cache_line_size, cleaning_policy, | ||
promotion_policy, seq_cutoff_policy): | ||
""" | ||
title: Security test for loading cache with corrupted metadata. | ||
description: | | ||
Validate the ability of Open CAS to load cache instance and work if metadata is corrupted. | ||
pass_criteria: | ||
- If metadata is recognized as corrupted, load operation should be aborted. | ||
- If not then executing I/O operations on CAS device should not result in kernel panic. | ||
""" | ||
|
||
module = "brd" | ||
iteration_per_config = 1000 | ||
|
||
with TestRun.step("Prepare RAM devices for test."): | ||
load_kernel_module( | ||
module, { | ||
"rd_nr": 2, | ||
"rd_size": int(Size(100, Unit.MiB).get_value(Unit.KiB)) | ||
} | ||
) | ||
if not is_kernel_module_loaded(module): | ||
TestRun.fail(f"Cannot load '{module}' module. Module is unsupported.") | ||
|
||
cache_dev_link = Symlink.get_symlink( | ||
link_path="/dev/disk/by-id/cache_dev", | ||
target="/dev/ram0", | ||
create=True | ||
) | ||
core_dev_link = Symlink.get_symlink( | ||
link_path="/dev/disk/by-id/core_dev", | ||
target="/dev/ram1", | ||
create=True | ||
) | ||
|
||
cache_dev = Device(cache_dev_link.full_path) | ||
core_dev = Device(core_dev_link.full_path) | ||
|
||
for iteration in TestRun.iteration( | ||
range(iteration_per_config), | ||
f"Corrupt metadata {iteration_per_config} times." | ||
): | ||
|
||
with TestRun.step("Prepare cache and core."): | ||
cache = casadm.start_cache(cache_dev, cache_mode, cache_line_size, force=True) | ||
core = casadm.add_core(cache, core_dev) | ||
metadata_size = int(cache.metadata_size_on_disk.get_value()) | ||
|
||
with TestRun.step("Configure cache."): | ||
cache.set_cleaning_policy(cleaning_policy) | ||
cache.set_promotion_policy(promotion_policy) | ||
cache.set_seq_cutoff_policy(seq_cutoff_policy) | ||
|
||
with TestRun.step("Fill cache with random data."): | ||
Dd().input('/dev/urandom') \ | ||
.output(core.path) \ | ||
.block_size(Size(1, Unit.Blocks512)) \ | ||
.oflag('direct') \ | ||
.run() | ||
|
||
with TestRun.step("Stop cache without flush."): | ||
cache.stop(True) | ||
|
||
with TestRun.step("Corrupt metadata."): | ||
corrupt_metadata(cache_dev, iteration, metadata_size) | ||
|
||
with TestRun.step("Try to load cache."): | ||
loaded = False | ||
try: | ||
casadm.load_cache(cache_dev) | ||
loaded = True | ||
TestRun.LOGGER.info("Cache is loaded.") | ||
except CmdException: | ||
TestRun.LOGGER.info("Cache is not loaded.") | ||
|
||
if loaded: | ||
with TestRun.step("Run random I/O traffic to cache."): | ||
try: | ||
Dd().input('/dev/urandom') \ | ||
.output(core.path) \ | ||
.block_size(Size(1, Unit.Blocks512)) \ | ||
.oflag('direct') \ | ||
.run() | ||
except CmdException: | ||
TestRun.LOGGER.error("Sending I/O requests to cache caused error.") | ||
cache.stop() | ||
break | ||
|
||
with TestRun.step("Stop cache."): | ||
cache.stop() | ||
|
||
with TestRun.step("Delete symlinks and unload 'brd' module."): | ||
remove(cache_dev.path, True) | ||
remove(core_dev.path, True) | ||
unload_kernel_module('brd') | ||
|
||
|
||
def corrupt_metadata(cache_dev: Device, iteration: int, metadata_size: int): | ||
superblock_max_bytes = int(Size(8, Unit.KiB).get_value()) | ||
number_of_bits_to_corrupt = random.randint(1, 10) | ||
corrupted_bytes = [] | ||
for i in range(number_of_bits_to_corrupt): | ||
random_mask = 1 << random.randrange(0, 7) | ||
random_offset = random.randrange( | ||
0, superblock_max_bytes if iteration % 100 == 0 else metadata_size | ||
) | ||
corrupted_bytes.append(random_offset) | ||
corrupt_bits(cache_dev, random_mask, random_offset) | ||
corrupted_bytes.sort() | ||
TestRun.LOGGER.info(f"Corrupted bytes: {corrupted_bytes}") | ||
|
||
|
||
def corrupt_bits(cache_dev: Device, mask: int, offset: int): | ||
output = TestRun.executor.run(f"xxd -bits -len 1 -seek {offset} -postscript {cache_dev.path}") | ||
corrupt_cmd = f"printf '%02x' $((0x{output.stdout}^{mask}))" | ||
corrupt_cmd = f"{corrupt_cmd} | xxd -revert -postscript -seek {offset} - {cache_dev.path}" | ||
TestRun.executor.run_expect_success(corrupt_cmd) |