1
0
This commit is contained in:
Sytnyk Yehor
2025-05-15 13:11:19 +03:00
parent 4c01c2fe11
commit 9fb9f66f27
26 changed files with 730 additions and 0 deletions

View File

@ -0,0 +1,7 @@
> [!NOTE]
> Викладач: Мельникова Р. В.
>
> Оцінка: -
> [!TIP]
> Виконано для Linux в команді.

View File

@ -0,0 +1,21 @@
title: Керування пам'яттю. Частина 2
subject: ОС
doctype: ЛБ
worknumber: 6
mentors:
- name: Мельнікова Р. В.,
gender: f,
degree: доц. каф. ПІ,
edu_program: &EDU ПЗПІ
university: ХНУРЕ
authors:
- name: Ситник Є. С.
course: 2
edu: *EDU
gender: m
group: 23-2
- name: Малишкін. А. С.
course: 2
edu: *EDU
gender: m
group: 23-2

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 968 KiB

View File

@ -0,0 +1,17 @@
CompileFlags:
Add: [-Wall, -Wextra, -std=c++23, -DBUILD_SHARED]
CompilationDatabase: build/
Diagnostics:
UnusedIncludes: Strict
InlayHints:
Enabled: Yes
ParameterNames: Yes
DeducedTypes: Yes
Index:
Background: Build
Hover:
ShowAKA: Yes

View File

@ -0,0 +1,15 @@
CXX := clang++
CXXFLAGS := -std=c++23 -Iinclude -Wall -Wextra
LDFLAGS := -lcrypto
TARGET := build/app
SRC := main.cpp
.PHONY: all clean
all:
@mkdir -p $(dir $(TARGET))
@$(CXX) $(CXXFLAGS) $(SRC) -o $(TARGET) $(LDFLAGS)
clean:
rm -rf build

View File

@ -0,0 +1,37 @@
#pragma once
#ifndef CACHE_LRU_HPP
#define CACHE_LRU_HPP
#include <cstdint>
#include <list>
#include <unordered_map>
#include <vector>
namespace cache {
class lru {
public:
lru(int32_t line_count, int32_t associativity);
~lru() = default;
bool access(uint64_t address);
private:
struct block_entry {
uint64_t tag;
};
int32_t _lines;
int32_t _assoc;
std::vector<std::list<block_entry>> _cache_lines;
std::vector<std::unordered_map<uint64_t, std::list<block_entry>::iterator>>
_lookup;
};
} // namespace cache
#include "../src/cache.cpp"
#endif

View File

@ -0,0 +1,84 @@
#pragma once
#ifndef SEC_LOCKED_BUFFER_HPP
#define SEC_LOCKED_BUFFER_HPP
#include <cstdlib>
#include <cstring>
#include <stdexcept>
#include <sys/mman.h>
#include <unistd.h>
namespace sec {
template <typename T, std::size_t N> class locked_buffer {
public:
locked_buffer() {
this->size_ = N * sizeof(T);
this->ptr_ = static_cast<T *>(std::malloc(this->size_));
if (!this->ptr_)
throw std::bad_alloc();
if (mlock(this->ptr_, this->size_) != 0) {
std::free(this->ptr_);
throw std::runtime_error("mlock failed");
}
}
~locked_buffer() noexcept {
cleanse();
if (this->ptr_) {
munlock(this->ptr_, this->size_);
std::free(this->ptr_);
}
}
locked_buffer(const locked_buffer &) = delete;
locked_buffer &operator=(const locked_buffer &) = delete;
locked_buffer(locked_buffer &&other) noexcept
: ptr_(other.ptr_), size_(other.size_) {
other.ptr_ = nullptr;
other.size_ = 0;
}
locked_buffer &operator=(locked_buffer &&other) noexcept {
if (this != &other) {
cleanse();
if (this->ptr_) {
munlock(this->ptr_, this->size_);
std::free(this->ptr_);
}
this->ptr_ = other.ptr_;
this->size_ = other.size_;
other.ptr_ = nullptr;
other.size_ = 0;
}
return *this;
}
T *data() noexcept { return this->ptr_; }
const T *data() const noexcept { return this->ptr_; }
std::size_t size() const noexcept { return N; }
T &operator[](std::size_t i) { return this->ptr_[i]; }
const T &operator[](std::size_t i) const { return this->ptr_[i]; }
T *begin() noexcept { return this->ptr_; }
T *end() noexcept { return this->ptr_ + N; }
const T *begin() const noexcept { return this->ptr_; }
const T *end() const noexcept { return this->ptr_ + N; }
private:
void cleanse() noexcept {
if (this->ptr_) {
std::memset(this->ptr_, 0, this->size_);
}
}
T *ptr_{nullptr};
std::size_t size_{0};
};
} // namespace sec
#endif

View File

@ -0,0 +1,44 @@
#pragma once
#ifndef PAGE_REPLACEMENT_HPP
#define PAGE_REPLACEMENT_HPP
#include <cstdint>
#include <vector>
namespace paging {
class replacer {
public:
explicit replacer(int32_t frame_count) : _frames(frame_count) {}
virtual ~replacer() = default;
virtual int32_t run(const std::vector<int32_t> &pages) = 0;
protected:
int32_t _frames;
};
class optimal_replacer : public replacer {
public:
using replacer::replacer;
int32_t run(const std::vector<int32_t> &pages) override;
};
class clock_replacer : public replacer {
public:
using replacer::replacer;
int32_t run(const std::vector<int32_t> &pages) override;
};
class lru_replacer : public replacer {
public:
using replacer::replacer;
int32_t run(const std::vector<int32_t> &pages) override;
};
} // namespace paging
#include "../src/paging.cpp"
#endif

View File

@ -0,0 +1,68 @@
#pragma once
#ifndef PASSWORD_HPP
#define PASSWORD_HPP
#include <cstdint>
#include <string>
#include <string_view>
#include "locked_buffer.hpp"
namespace sec {
enum class strength_t : uint8_t {
very_weak = 0,
weak,
moderate,
strong,
very_strong
};
class password_hash;
class password {
public:
explicit password(std::string pass);
std::string_view view() const noexcept;
static sec::password from_console();
static sec::password from_string(const std::string &pass);
sec::password_hash to_hash() const;
private:
sec::locked_buffer<char, 128> _buf;
std::size_t _len{};
};
class password_hash {
public:
explicit password_hash(std::string hash);
const std::string &get() const noexcept;
bool verify(const sec::password &pwd) const;
private:
std::string _hash;
};
class manager {
public:
static bool has_lower(const sec::password &pwd);
static bool has_upper(const sec::password &pwd);
static bool has_digit(const sec::password &pwd);
static bool has_spec_symbol(const sec::password &pwd);
static std::string hash_password(const sec::password &pwd);
static sec::strength_t evaluate_strength(const sec::password &pwd);
static uint64_t time_to_crack(const sec::password &pwd);
static std::string format_duration(uint64_t seconds);
};
} // namespace sec
#include "../src/password.cpp"
#endif

View File

@ -0,0 +1,73 @@
#include <iostream>
#include <print>
#include <vector>
#include "include/cache.hpp"
#include "include/paging.hpp"
#include "include/password.hpp"
#include <openssl/sha.h>
int main() {
std::vector<int> pages = {7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3};
int frames = 3;
paging::optimal_replacer opt(frames);
paging::clock_replacer clk(frames);
paging::lru_replacer lru(frames);
std::println("Optimal faults: {}", opt.run(pages));
std::println("Clock faults: {}", clk.run(pages));
std::println("LRU faults: {}", lru.run(pages));
std::println("\nCache LRU Simulation:");
cache::lru cache(128, 4);
std::vector<uint64_t> addresses = {
0, 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192,
};
int32_t hits = 0, misses = 0;
for (auto addr : addresses) {
if (cache.access(addr)) {
++hits;
std::println("Address 0x{:x}:\t HIT", addr);
} else {
++misses;
std::println("Address 0x{:x}:\t MISS", addr);
}
}
std::println("Hits: {}, Misses: {}\n", hits, misses);
auto pwd = sec::password::from_console();
auto strength = sec::manager::evaluate_strength(pwd);
auto crack_time = sec::manager::time_to_crack(pwd);
auto crack_str = sec::manager::format_duration(crack_time);
std::print("Password strength: ");
switch (strength) {
case sec::strength_t::very_weak:
std::print("Very Weak");
break;
case sec::strength_t::weak:
std::print("Weak");
break;
case sec::strength_t::moderate:
std::print("Moderate");
break;
case sec::strength_t::strong:
std::print("Strong");
break;
case sec::strength_t::very_strong:
std::print("Very Strong");
break;
}
std::print("\nEstimated time to crack: {} secs\n({})\n", crack_time,
crack_str);
std::string input;
std::print("\nEnter password to verify: ");
std::getline(std::cin, input);
auto input_pwd = sec::password::from_string(input);
if (pwd.to_hash().verify(input_pwd)) {
std::print("Password verified successfully.\n");
}
}

View File

@ -0,0 +1,38 @@
#pragma once
#include "../include/cache.hpp"
#include <cmath>
namespace cache {
cache::lru::lru(int32_t line_count, int32_t associativity)
: _lines(line_count), _assoc(associativity), _cache_lines(line_count),
_lookup(line_count) {}
bool cache::lru::access(uint64_t address) {
uint64_t line = (address >> 6) % this->_lines;
uint64_t tag =
address >> (6 + static_cast<uint64_t>(std::log2(this->_lines)));
auto &line_list = this->_cache_lines[line];
auto &line_map = this->_lookup[line];
auto it = line_map.find(tag);
if (it != line_map.end()) {
line_list.splice(line_list.begin(), line_list, it->second);
line_map[tag] = line_list.begin();
return true;
}
if (static_cast<int32_t>(line_list.size()) >= this->_assoc) {
auto last = line_list.back();
line_map.erase(last.tag);
line_list.pop_back();
}
line_list.push_front({tag});
line_map[tag] = line_list.begin();
return false;
}
} // namespace cache

View File

@ -0,0 +1,115 @@
#pragma once
#include "../include/paging.hpp"
#include <algorithm>
#include <limits>
#include <ranges>
#include <unordered_map>
namespace paging {
int32_t paging::optimal_replacer::run(const std::vector<int32_t> &pages) {
int32_t faults = 0;
std::vector<int32_t> frames;
frames.reserve(static_cast<size_t>(this->_frames));
for (size_t i : std::views::iota(size_t(0), pages.size())) {
int32_t p = pages[i];
if (std::find(frames.begin(), frames.end(), p) != frames.end())
continue;
++faults;
if (static_cast<int32_t>(frames.size()) < this->_frames) {
frames.push_back(p);
} else {
int32_t idx_to_replace = 0;
size_t farthest = 0;
for (int32_t j : std::views::iota(0, this->_frames)) {
size_t k = i + 1;
while (k < pages.size() && pages[k] != frames[j])
++k;
size_t dist =
(k < pages.size() ? k : std::numeric_limits<size_t>::max());
if (dist > farthest) {
farthest = dist;
idx_to_replace = j;
}
}
frames[idx_to_replace] = p;
}
}
return faults;
}
int32_t paging::clock_replacer::run(const std::vector<int32_t> &pages) {
int32_t faults = 0;
struct entry {
int32_t page;
bool ref;
};
std::vector<entry> frames;
frames.reserve(static_cast<size_t>(this->_frames));
int32_t hand = 0;
for (int32_t p : pages) {
bool hit = false;
for (auto &e : frames) {
if (e.page == p) {
e.ref = true;
hit = true;
break;
}
}
if (hit)
continue;
++faults;
if (static_cast<int32_t>(frames.size()) < this->_frames) {
frames.push_back({p, true});
} else {
while (true) {
if (!frames[hand].ref) {
frames[hand] = {p, true};
hand = (hand + 1) % this->_frames;
break;
}
frames[hand].ref = false;
hand = (hand + 1) % this->_frames;
}
}
}
return faults;
}
int32_t paging::lru_replacer::run(const std::vector<int32_t> &pages) {
int32_t faults = 0;
std::vector<int32_t> frames;
std::unordered_map<int32_t, int32_t> last_used;
frames.reserve(static_cast<size_t>(this->_frames));
for (int32_t i : std::views::iota(0, static_cast<int32_t>(pages.size()))) {
int32_t p = pages[i];
if (std::find(frames.begin(), frames.end(), p) != frames.end()) {
last_used[p] = i;
continue;
}
++faults;
if (static_cast<int32_t>(frames.size()) < this->_frames) {
frames.push_back(p);
} else {
int32_t lru_page = frames[0];
int32_t min_idx = last_used[lru_page];
for (int32_t q : frames) {
if (last_used[q] < min_idx) {
min_idx = last_used[q];
lru_page = q;
}
}
auto it = std::find(frames.begin(), frames.end(), lru_page);
if (it != frames.end())
*it = p;
}
last_used[p] = i;
}
return faults;
}
} // namespace paging

View File

@ -0,0 +1,151 @@
#pragma once
#include "../include/password.hpp"
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <openssl/sha.h>
#include <print>
#include <ranges>
#include <sstream>
namespace sec {
sec::password::password(std::string pass) {
if (pass.size() > _buf.size()) {
throw std::length_error("Password too long");
}
_len = pass.size();
std::copy(pass.data(), pass.data() + _len, _buf.begin());
}
std::string_view sec::password::view() const noexcept {
return std::string_view(_buf.data(), _len);
}
sec::password sec::password::from_console() {
std::string p1, p2;
while (true) {
std::print("Enter password: ");
std::getline(std::cin, p1);
std::print("Re-enter password: ");
std::getline(std::cin, p2);
if (p1 != p2) {
std::print("Passwords do not match. Try again.\n");
continue;
}
if (sec::manager::evaluate_strength(sec::password(p1)) ==
sec::strength_t::very_weak) {
std::print("Password is too weak. Try again.\n");
continue;
}
break;
}
return sec::password(p1);
}
sec::password sec::password::from_string(const std::string &pass) {
if (sec::manager::evaluate_strength(sec::password(pass)) ==
sec::strength_t::very_weak) {
std::print("Password is too weak. Try again.\n\n");
throw std::invalid_argument("Weak password");
}
return sec::password(pass);
}
sec::password_hash::password_hash(std::string hash) : _hash(std::move(hash)) {}
const std::string &sec::password_hash::get() const noexcept { return _hash; }
bool sec::password_hash::verify(const sec::password &pwd) const {
return sec::manager::hash_password(pwd) == _hash;
}
sec::password_hash sec::password::to_hash() const {
return sec::password_hash(sec::manager::hash_password(*this));
}
bool sec::manager::has_lower(const sec::password &pwd) {
auto sv = pwd.view();
return std::any_of(sv.begin(), sv.end(), ::islower);
}
bool sec::manager::has_upper(const sec::password &pwd) {
auto sv = pwd.view();
return std::any_of(sv.begin(), sv.end(), ::isupper);
}
bool sec::manager::has_digit(const sec::password &pwd) {
auto sv = pwd.view();
return std::any_of(sv.begin(), sv.end(), ::isdigit);
}
bool sec::manager::has_spec_symbol(const sec::password &pwd) {
auto sv = pwd.view();
return std::any_of(sv.begin(), sv.end(),
[](unsigned char c) { return std::ispunct(c); });
}
sec::strength_t sec::manager::evaluate_strength(const sec::password &pwd) {
uint8_t score = 0;
if (has_lower(pwd))
++score;
if (has_upper(pwd))
++score;
if (has_digit(pwd))
++score;
if (has_spec_symbol(pwd))
++score;
if (pwd.view().size() >= 8)
++score;
if (score != 0)
score--;
uint8_t max_val = static_cast<uint8_t>(sec::strength_t::very_strong);
uint8_t idx = std::min(score, max_val);
return static_cast<sec::strength_t>(idx);
}
uint64_t sec::manager::time_to_crack(const sec::password &pwd) {
auto sv = pwd.view();
if (sv.empty())
return 0;
int charset = (has_lower(pwd) ? 26 : 0) + (has_upper(pwd) ? 26 : 0) +
(has_digit(pwd) ? 10 : 0) + (has_spec_symbol(pwd) ? 33 : 0);
constexpr double RATE = 1e7;
double combos = std::pow(static_cast<double>(charset), sv.size());
return static_cast<uint64_t>(combos / 2.0 / RATE);
}
std::string sec::manager::format_duration(uint64_t seconds) {
const uint64_t u[] = {31536000, 2592000, 86400, 3600, 60, 1};
const char *lbl[] = {"years", "months", "days", "hours", "mins", "secs"};
std::string s;
for (int i : std::views::iota(0, 6)) {
uint64_t v = seconds / u[i];
if (v) {
s += std::to_string(v) + " " + lbl[i] + ((i == 5) ? "" : " ");
seconds %= u[i];
}
}
return s.empty() ? "0 secs" : s;
}
std::string sec::manager::hash_password(const sec::password &pwd) {
auto sv = pwd.view();
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256(reinterpret_cast<const unsigned char *>(sv.data()), sv.size(), hash);
std::ostringstream oss;
for (int32_t i : std::views::iota(0, SHA256_DIGEST_LENGTH)) {
oss << std::hex << std::setw(2) << std::setfill('0')
<< static_cast<int32_t>(hash[i]);
}
return oss.str();
}
} // namespace sec

View File

@ -0,0 +1,60 @@
#import "@local/nure:0.1.0": *
#show: pz-lb.with(..yaml("doc.yaml"))
#v(-spacing)
== Мета роботи
Вивчити особливості захисту критичних даних, використання динамічної та кеш пам'яті.
== Хід роботи
#v(-spacing)
=== Порівняти алгоритми заміщення сторінок (оптимальний, годинник, LRU).
Заміщення сторінок - це процес, який використовується в операційних системах для керування віртуальною пам'яттю, коли фізичної пам'яті недостатньо. Коли потрібна сторінка, якої немає в оперативній пам'яті, операційна система вибирає одну зі сторінок, що вже знаходяться в пам'яті, і замінює її потрібною сторінкою.
Сучасні операційні системи використовують різні підходи для визначення сторінок, які можна замістити.
Розглянемо деякі алгоритми заміщення сторінок:
+ оптимальний алгоритм (також відомий як алгоритм Беладі) -- теоретичний алгоритм, який досягає найменшої кількості промахів сторінок. Він працює, замінюючи сторінку, яка не буде використовуватися найдовший період часу в майбутньому. Хоча цей алгоритм є оптимальним, він не може бути реалізований на практиці, оскільки вимагає знання майбутньої послідовності звернень до сторінок;
+ алгоритм годинник (також відомий як алгоритм "другого шансу") -- зберігає список сторінок у пам'яті, кожна з яких має біт використання. Коли відбувається промах сторінки і потрібно замінити сторінку, алгоритм перевіряє біт використання поточної сторінки, на яку вказує покажчик. Якщо біт використання дорівнює 1, він скидається до 0, і покажчик переміщується до наступної сторінки. Цей процес повторюється до тих пір, поки не буде знайдено сторінку з бітом використання, що дорівнює 0, яка і замінюється. Такий підхід надає "другий шанс" сторінкам, які нещодавно використовувалися, запобігаючи їхньому швидкому витісненню;
+ алгоритм LRU (Least Recently Used) -- замінює сторінку, яка найдовше не використовувалася. Він базується на припущенні, що сторінки, які використовувалися нещодавно, ймовірно, будуть використовуватися знову в найближчому майбутньому, а ті, що давно не використовувалися, мають меншу ймовірність повторного звернення. Для реалізації LRU необхідно відстежувати час останнього звернення до кожної сторінки в пам'яті. Коли виникає потреба у заміщенні, вибирається сторінка з найдавнішим часом останнього використання.
#figure(image("img/pages-optimal.png", width: 90%), caption: [Оптимальний алгоритм заміщення сторінок])
#figure(image("img/pages-clock.png", width: 100%), caption: [Алгоритм заміщення сторінок "Годинник"])
#figure(image("img/pages-lru.png", width: 80%), caption: [Алгоритм заміщення сторінок "LRU"])
Протестуємо всі алгоритми із однаковими тестовими даними.
#figure(image("img/pages-test.png", width: 80%), caption: [Код тестування алгоритмів])
#figure(image("img/pages-result.png"), caption: [Результати тестування])
Можемо побачити, що оптимальний алгоритм показує найкращі результати.
=== Реалізувати алгоритм LRU для роботи з кешем для стандартних параметрів кешу
Кешування - це техніка збереження часто використовуваних даних у швидшій пам'яті (кеші) для зменшення часу доступу. Оскільки кеш має обмежений розмір, при його заповненні виникає потреба у визначенні того, які дані слід видалити для звільнення місця для нових.
#figure(image("img/cache-lru.png", width: 87%), caption: [Алгоритм "LRU" для доступу до кешу])
#figure(image("img/cache-test.png", width: 90%), caption: [Код тестування])
#figure(image("img/cache-result.png", width: 45%), caption: [Результати тестування])
=== Реалізувати функції встановлення паролю та перевірки паролю. При складанні функцій забезпечте безпечне зберігання паролів.
Безпечність пароля можна визначити за часом, що потрібен для його зламу. Чим більше часу необхідно на злам, тим безпечніший пароль.
#figure(
image("img/pass-table.png", width: 70%),
caption: [Таблиця відповідності складності паролю до часу, що необхідний на злам],
)
Критерії надійності паролю зазначені в таблиці детально обговорюються в дослідженні Hive Systems, яке оновлюється кожен рік починаючи з 2020 (#link("https://www.hivesystems.com/blog/are-your-passwords-in-the-green"))
#figure(image("img/pass-check.png", width: 70%), caption: [Функція перевірки надійності пароля])
Для забезпечення безпеки пароля в пам'яті під час гешування було використано стандартні POSIX функції "mlock" та "munlock".
#figure(image("img/pass-lock.png", width: 74%), caption: [Функції блокування та розблокування пам'яті])
Для гешування паролю ми використали алгоритм "SHA256" із бібліотеки "openssl", яка є стандартним вибором для всього, що так чи інакше пов'язано із криптографією.
#figure(image("img/pass-hash.png", width: 74%), caption: [Функція гешування паролю])
== Висновки
Під час даної лабораторної роботи ми вивчили особливості захисту критичних даних, використання динамічної та кеш пам'яті.
#show: appendices_style