Научная статья на тему 'УГРОЗА ИНФОРМАЦИОННОЙ БЕЗОПАСНОСТИ ПРИ ОПТИМИЗАЦИИ КОМПИЛЯТОРОМ ФУНКЦИИ MEMSET В ЯЗЫКЕ ПРОГРАММИРОВАНИЯ C++'

УГРОЗА ИНФОРМАЦИОННОЙ БЕЗОПАСНОСТИ ПРИ ОПТИМИЗАЦИИ КОМПИЛЯТОРОМ ФУНКЦИИ MEMSET В ЯЗЫКЕ ПРОГРАММИРОВАНИЯ C++ Текст научной статьи по специальности «Компьютерные и информационные науки»

CC BY
57
14
i Надоели баннеры? Вы всегда можете отключить рекламу.
Ключевые слова
ОПТИМИЗАЦИЯ / КОМПИЛЯТОР / УГРОЗА / РАЗРАБОТКА

Аннотация научной статьи по компьютерным и информационным наукам, автор научной работы — Семенчев Григорий Владимирович, Борзенкова Светлана Юрьевна

В статье исследована угроза безопасности, возникающая из-за оптимизации компилятором функции memset при разработке программного обеспечения на языке программирования С++.

i Надоели баннеры? Вы всегда можете отключить рекламу.

Похожие темы научных работ по компьютерным и информационным наукам , автор научной работы — Семенчев Григорий Владимирович, Борзенкова Светлана Юрьевна

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

THREAT TO INFORMATION SECURITY WHEN THE COMPILER OPTIMIZES THE MEMSET FUNCTION IN THE C++ PROGRAMMING LANGUAGE

The article investigates the security threat that occurs due to the compiler’s optimization of memset function while developing software in C++ programming language.

Текст научной работы на тему «УГРОЗА ИНФОРМАЦИОННОЙ БЕЗОПАСНОСТИ ПРИ ОПТИМИЗАЦИИ КОМПИЛЯТОРОМ ФУНКЦИИ MEMSET В ЯЗЫКЕ ПРОГРАММИРОВАНИЯ C++»

2. Б.С. Алешин, А.А. Афонин, К.К. Веремеенко, Б.В. Кошелев, В.Е. Плеханов, В.А. Тихонов, А.В. Тювин, Е.П. Федосеев, А.И. Черноморский. Ориентация и навигация подвижных объектов: современные информационные технологии. М.: ФИЗМАТЛИТ, 2006. 424 с.

3. Bortz J.E. A new concept in strapdown inertial navigation [Ph.D. dissertation], Massachusetts In-stitiute of Technology, Cambridge, Mass, USA, 1969.

4. Savage P.G. Strapdown inertial navigation integration algorithm design. Part 1. Attitude algorithms // Journal of Guidance, Control, and Dynamics, 1998. vol. 21, no. 1. P. 19-28.

5. Емельянцев Г.И., Степанов А.П. Интегрированные инерциально-спутниковые системы ориентации и навигации/ Под общей ред. акад. РАН В.Г. Пешехонова. СПб.: ГНЦ РФ АО «Концерн «ЦНИИ «Электроприбор», 2016. 394 с.

Матвеев Валерий Владимирович, д-р. техн. наук, профессор, ведущий научный сотрудник молодежной лаборатории инерциальных датчиков первичной информации, систем ориентации и навигации, matweew.valery@yandex.ru, Россия, Тула, Тульский государственный университет,

Кузнецов Илья Дмитриевич, магистрант, mrpomidorko66566@gmail.ru, Россия, Тула, Тульский государственный университет

WORK SIMULA TION COMPLEX INERTIAL-SATELLITE NAVIGATION SYSTEMS

V.V. Matveev, I.D. Kuznetsov

The results of the operation of the complex for modeling the functioning of an inertial satellite navigation system are presented. A block diagram of the system is given, with the help of which a description of the interaction of its internal blocks is given. A mathematical model of the trajectory in the form of a spatial "eight" is given. The performance of the complex is analyzed using the example of SINS with micromechanical sensitive elements.

Key words: simulation complex, inertial satellite navigation system, loosely coupled integration

scheme.

Matveev Valery Vladimirovich, doctor of technical sciences, professor, leading researcher of the Youth Laboratory of inertial sensors of primary information of orientation and navigation systems, mat-weew.valery@yandex.ru, Russia, Tula, Tula State University,

Kuznetsov Ilya Dmitrievich, master, mrpomidorko66566@gmail. com, Russia, Tula, Tula State

University

УДК 04.056

DOI: 10.24412/2071-6168-2023-3-520-526

УГРОЗА ИНФОРМАЦИОННОЙ БЕЗОПАСНОСТИ ПРИ ОПТИМИЗАЦИИ КОМПИЛЯТОРОМ ФУНКЦИИ MEMSET В ЯЗЫКЕ ПРОГРАММИРОВАНИЯ С++

Г.В. Семенчев, С.Ю. Борзенкова

В статье исследована угроза безопасности, возникающая из-за оптимизации компилятором функции memset при разработке программного обеспечения на языке программирования С++.

Ключевые слова: оптимизация, компилятор, угроза, разработка.

Современные предприятия как коммерческие, так и государственные, зачастую, разрабатывают собственное программное обеспечение (ПО). Очень часто разработка таких утилит осуществляется на С++, который входит в тройку самых популярных языков программирования.

С++ - это быстрый многофункциональный язык, скорость работы которого обеспечивается компилируемостью, относительной низкоуровневостью и оптимизациями компилятора. Однако последнее может привести к трудноотлаживаемым ошибкам, способным вывести из строя программное обеспечение или создать угрозу информационной безопасности предприятия. Об одной такой будет рассказано в данной статье.

Существует две категории языков программирования: интерпретируемые (Python, PHP) и компилируемые (Haskell, Rust). С++ относится к последней группе. Это означает, что специальная программа (компилятор) превращает код, написанный разработчиком, в исполняемый файл, который после этого запускается.

Не существует единственного компилятора, который использовался бы на всех платформах повсеместно. Например, на Windows наиболее популярным является MSVC, созданный компанией Microsoft и используемый в основном совместно со средой разработки Visual Studio. В Linux и MacOS доминируют GCC и Clang.

Для того чтобы поведение программ, собранных разными компиляторами, не отличалось, существуют жесткие правила (стандарты), которые должны соблюдаться всеми компиляторами. Однако с целью повышения эффективности выполнения кода, компиляторы могут оптимизировать (улучшить) его на основе внутренней логики, которая стандартами С++ никак не регламентируется. Это может приводить к серьёзным ошибкам и уязвимостям в случае незнания разработчиком особенностей платформы, на которой ведётся разработка.

Рассмотрим синтетический пример кода, который будет оптимизирован компилятором. Для этого напишем две функции: «square», которая умножает параметр функции на себя самого, и «func», которая к результату вызова «square» прибавляет число 10: std::int64_t square(std::int64_t a) { return a * a; } std::int64_t func(std::int64_t x) { return square(x) - 10; }

Теперь скомпилируем код при помощи MSVC, не включая режим оптимизации, и посмотрим, во что превратятся написанные функции: a$ = 8

_int64 square(_int64) PROC

mov QWORD PTR [rsp+8], rcx mov rax, QWORD PTR a$[rsp] imul rax, QWORD PTR a$[rsp] ret 0

_int64 square(_int64) ENDP

x$ = 48

_int64 func(_int64) PROC

sub rsp, 40

mov rcx, QWORD PTR x$[rsp]

call_int64 square(_int64)

sub rax, 10 add rsp, 40 ret 0

_int64 func(_int64) ENDP

Кажется, что код работает строго так, как написано: функция «func» вызывает «square» при помощи инструкции CALL и от результата вычитает число 10, после чего выполняется инструкция RET, передающая управление вверх по стеку вызовов.

Однако стоит разрешить MSVC оптимизировать программу (для этого есть параметр компилятора /O), как ассемблерный код, в который она превращается, становится меньше: x$ = 8

_int64 func(_int64) PROC

imul rcx, rcx

lea rax, QWORD PTR [rcx-10] ret 0

_int64 func(_int64) ENDP

a$ = 8

_int64 square(_int64) PROC

imul rcx, rcx mov rax, rcx ret 0

_int64 square(_int64) ENDP

Компилятор правильно оптимизировал код. Можно сразу умножить параметр функции «func» на самого себя, не делая вызов функции square. Это позволяет уменьшить количество инструкций, выполняемых в коде, и сэкономить процессорное время.

В данном случае можно наблюдать, что вызов «square» был заменён компилятором на инструкцию IMUL. Результат по итогу выполнения «func» будет такой же, но скорость работы будет увеличена.

К сожалению, не всегда оптимизации компилятора помогают разработчикам. Иногда незнание особенностей работы компиляторов может приводить к серьёзным трудноотлавливаемым уязвимостям, способным привести, например к утечке конфиденциальных данных. Для демонстрации рассмотрим правильно написанный, с точки зрения стандарта С++, код, который не будет работать так, как задумывалось.

Предположим, что есть некоторый алгоритм хеширования паролей. Делать это будет функция «GenerateHash»:

Hash GenerateHash(std::int64_t hashSalt) {

Password pass = GetPass(); // .... Not important realization details Hash passHash{pass, hashSalt}; memset(&pass, 0, sizeof(Password));

return passHash; }

На первый взгляд не понятно, зачем перед выходом из функции вызывается memset для переменной pass, однако смысл в этом есть. Переменная pass хранит пароль, конфиденциальные данные. После выхода из функции память, занимаемая переменной pass, освободится и станет доступной для использования, однако, она продолжит хранить в себе последние записанные данные. Чтобы избежать чтения освободившейся памяти с конфиденциальными данными, разработчик специально вызывает функцию memset, которая «затирает» всю память, занимаемую переменной pass, нулями.

Скомпилируем данный код при помощи GCC 12.2 и посмотрим на ассемблерный код функции:

GenerateHash(long)

push rbp

mov rbp, rsp

sub rsp, 48

mov QwORD PTR [rbp-40], rdi call GetPass()

mov QWORD PTR [rbp-40], rax

mov QWORD PTR [rbp-8], rdx

mov rdx, QWORD PTR [rbp-40]

lea rcx, [rbp-16]

lea rax, [rbp-32]

mov rsi, rcx

mov rdi, rax

call Hash::Hash()

lea rax, [rbp-16]

mov edx, 16

mov esi, 0

mov rdi, rax

call memset

mov rax, QWORD PTR [rbp-32] mov rdx, QWORD PTR [rbp-24]

leave ret

Кажется, что всё в порядке: функция memset вызывается при помощи инструкции CALL, память затирается, и после выхода из функции злоумышленник никак не получит «остаточные» данные. Однако это не так. Включим оптимизации компилятора GCC на самом минимальном уровне при помощи параметра -O1 и посмотрим на изменения в ассемблерном коде: GenerateHash(long): push rbx sub rsp, 32 mov rbx, rdi call GetPass()

mov QWORD PTR [rsp+16], rax

mov QWORD PTR [rsp+24], rdx

mov rdx, rbx

lea rsi, [rsp+16]

mov rdi, rsp

call Hash::Hash()

mov rax, QWORD PTR [rsp]

mov rdx, QWORD PTR [rsp+8]

add rsp, 32

pop rbx

ret

В оптимизированном коде появилась уязвимость: вызов функции memset был удалён компилятором GCC, как бессмысленный. Современные компиляторы достаточно умные и используют различные эвристики, чтобы понять, что программист вызывает функцию, которая не имеет эффекта. С точки зрения компилятора вызов memset бессмысленный: изменяется переменная pass, которая уже на следующей инструкции будет удалена, поскольку является локальной.

Однако на деле это приводит к чрезвычайно трудноуловимой уязвимости: разработчик считает, что данные пароля уничтожаются перед выходом из функции GenerateHash, но на самом деле этого не происходит. И злоумышленник всё также имеет возможность считать конфиденциальную информацию из освободившейся памяти.

Согласно реестру общих уязвимостей данная ошибка имеет классификатор CWE-14.

Существует несколько способов избежать оптимизации вызова memset. Самый надёжный -использовать готовое решение, предоставляемое стандартной библиотекой. К таким решениям относятся функции memset_s (добавлена в С++11) и RtlSecureZeroMemory (предоставляется WinAPI). Они предоставляют безопасную замену memset и для них отключена оптимизация компиляторами.

Заменим вызов memset на вызов RtlSecureZeroMemory и исследуем изменения ассемблерного кода (рис. 1).

17 call Password GetPass(void) ; GetPass

18 mov rS, rdi

19 lea rdx, QWORD PTR pass$[rs:]

20 mov rex, rbx

21 movups xmrnSj XMMWORD PTR [ax]

22 movups XMMWORD PTR passf[rs; ], xn }

23 call Hash::Hash(Password const _int64) ; Hash::H

24 xor eax, eax

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

25 lea rdi, QWORD PTR pass$[rsij]

26 mov ecx, 16

27 rep stosb

28 mov rax, rbx

29 mov rex, QWORD PTR _$ArrayPad$[rsp]

30 xor rex, rsp

31 call _security_check_cookie

32 mov rbx, QWORD PTR [rsp+112]

33 add rsp, 80 . 00000050H

34 pop rdi

35 ret 8

Рис. 1. Ассемблерный код после замены memset

Здесь видно, что компилятор всё же оптимизаровал вызов RtlSecureZeroMemory: в ассемблерном коде нет соответствующей инструкции CALL. Однако память всё равно «затирается» нулями. При внимательном просмотре можно заметить, что между вызовом конструктора объекта Hash и возвратом из функции есть несколько инструкций, ответственных за это: xor eax, eax

lea rdi, QWORD PTR pass$[rsp] mov ecx, 16 rep stosb mov rax, rbx

Данный ассемблерный код при помощи инструкции XOR помещает в регистр EAX ноль (инструкция MOV EAX, 0 не используется, поскольку она более медленная), после чего вычисляется адрес переменной pass при помощи инструкции LEA, полученный адрес помещается в регистр RDI. Далее в регистр ECX помещается размер, занимаемый переменной pass (в данном случае 16 байт). Далее инструкция REP STOSB выполняет цикл из ECX итераций, на каждой итерации помещая значение регистра EAX по адресу в RDI, инкрементируя регистр RDI на единицу для помещения значения в следующий байт на следующей итерации.

В своей документации по RtlSecureZeroMemory корпорация Microsoft говорит, что функция гарантирует, что память будет изменена в любом случае. Она не гарантирует, что сама функция не будет оптимизирована компилятором. Соответствующее поведение мы и наблюдаем на рис. 1.

К сожалению, memset_s предоставляется только теми реализациями стандартной библиотеки, у

которых есть предобъявленный макрос _STDC_WANT_LIB_EXT1_. Функция RtlSecureZeroMemory

же предоставляется только для WinAPI и может быть использована только на платформе Windows. Следовательно, данные готовые решения не являются универсальными.

Если разработчика заботит кросплатформенность, то можно реализовать собственную функцию, аналогичную RtlSecureZeroMemory. Есть как минимум два варианта реализации: используя в функции ключевое слово volatile и используя «барьер оптимизатора».

Рассмотрим один из множества возможных примеров собственной реализации аналога функции RtlSecureZeroMemory: [[maybe_unused]]

bool zero_memory(void *destPtr, size_t count) {

if (!destPtr)

{

return false; }

auto dest = static_cast<volatile char*>(destPtr);

for (size_t i = 0; i < count; ++i, ++dest) {

*dest = 0; }

return true; }

Здесь видно, что указатель, по которому будет проводиться запись, имеет квалификатор volatile, который запрещает компилятору оптимизировать данную переменную и гарантирует, что все операции чтения/записи с данной переменной будут выполнены «как есть». Это позволяет гарантировать, что по адресу dest в любом случае будет помещено count нулей и компилятор не будет этому мешать.

Заменим memset на функцию zero_memory и рассмотрим получившийся фрагмент оптимизированного ассемблерного кода: call Hash::Hash() lea rcx, QWORD PTR pass$[rsp] mov edx, 16 npad 5

$LL6@GenerateHa: mov BYTE PTR [rcx], 0 lea rcx, QWORD PTR [rcx+1] sub rdx, 1

jne SHORT $LL6@GenerateHa

mov rax, rdi

Эффект отличается от использования RtlSecureZeroMemory, однако, память всё же «затирается».

Вызов функции был оптимизирован, как и в случае с RtlSecureZeroMemory. Вместо него вставился цикл, который берет адрес переменной pass и помещает его в регистр RCX. Далее данный код записывает ноль по данному адресу и инкрементирует его на единицу, помещая результат обратно в регистр RCX. Значение в регистре RDX - это количество оставшихся итераций. На каждой итерации происходит декремент данной переменной.

Второй способ реализации своего аналога RtlSecureZeroMemory заключается в использовании «барьера памяти». Это специальная ассемблерная вставка, которая запрещает как-либо переупорядочивать код (в том числе выполнять некоторые оптимизации). Реализация zero_memory для такого случая:

void zero_memory(void *destPtr, size_t count) {

memset(destPtr, 0, count);

_asm__volatile_("" ::: "memory");

}

Для компилятора MSVC ассемблерная вставка заменяется на вызов специальной функции, выполняющей роль барьера памяти:

void zero_memory(void *destPtr, size_t count) {

memset(destPtr, 0, count);

_ReadWriteBarrier(); }

Рассмотрим изменения ассемблерного кода (рис. 2), после замены memset на получившуюся реализацию zero_memory.

18 call Hash::Hash(Password const&, long)@PLT 19 xorps xmm0, xmm0 20 movaps xmmword ptr ["sp], хшп;0

21 mov rax, qword ptr [rsp + 16]

22 mov rdx, qword ptr [rsp + 24]

23 add rsp, 32

24 pop rbx

25 ret

Рис. 2. Изменения ассемблерного кода

На рис. 2 можно заметить, что вызов zero_memory по-прежнему оптимизирован, но в память помещаются нули при помощи инструкции MOVAPS. Данная инструкция помещает по адресу из регистра RSP значение из регистра XMM0. При этом размер изменяемой памяти равен 16 байтам (размер переменной pass), о чем говорит слово XMMWORD. В регистр XMM0 перед этим помещается ноль инструкцией XORPS.

Ещё один способ отключить оптимизацию компилятора - использовать pragma-директивы. Например, для компилятора GCC они будут выглядеть так, как показано на рис. 3.

»pragma GCC push_options »pragma GCC optimize ("O0")[

void zero_memory(void »destPtr, size_t count) {

memsetfdestPtr, 0, count);

>

»pragma GCC popoptions

Рис. 3. Отключение оптимизаций через pragma-директивы

В таком случае оптимизации отключаются совсем и в ассемблерном коде будет прямой вызов zero_memory при помощи инструкции CALL (рис. 4).

call Hash::Hash(Password constü, long) [comp

mov rdi, rsp mov esi, 16

call zero memory(void*. unsigned long) mov rax, QWORD PTR [ ~+16J mov rdx, QWORD PTR [sp+24] add rsp, 32 pop rbx

Рис. 4. Ассемблерный код после отключения оптимизаций

Таким образом, использование функции memset для стирания остаточных данных перед выходом из функции - это потенциальная уязвимость, классифицирующаяся как CWE-14. Однако при знании разработчиком особенностей оптимизации компилятора, он может обойти или отключить их одним из рассмотренных способов, тем самым избежав опасности утечки данных.

Список литературы

1. Блог PVS-Studio. Безопасная очистка приватных данных. [Электронный ресурс] URL: https://habr.com/ru/companies/pvs-studio/articles/281072 (дата обращения: 06.02.2023).

2. Блог PVS-Studio. Самая опасная функция в мире С/С++. [Электронный ресурс] URL: https://pvs-studio.ru/ru/blog/posts/cpp/0360/?ysclid=lg2t48dge51981013 (дата обращения: 07.02.2023).

3. MITRE. Common Weaknesses Enumeration. CWE-14: Compiler Removal of Code to Clear Buffers. [Электронный ресурс] URL: https://cwe.mitre.org/data/definitions/14.html (дата обращения: 11.02.2023).

4. Стивен Прата. Язык программирования C++. Лекции и упражнения. Шестое издание. М.: Вильямс, 2012. 1248 с.

5. Бьярне Страуструп. Язык программирования С++. Четвёртое издание. М.: Лаборатория знаний, 2022. 1216 с.

6. Мейерс Скотт. Эффективный и современный С++: 42 рекомендации по использованию C++11 и C++14. М.: Вильямс, 2019. 304 с.

7. Роберт Сикорд. Безопасное программированое на С и С++. М.: Вильямс, 2016. 496 с.

8. Герберт Шилдт. Полный справочник по C++. М.: Вильямс, 2016. 800 с.

9. Роберт Седжвик. Алгоритмы С++. М.: Вильямс, 2019. 1056 с.

10. Беляков С.Л. Основы разработки программ на языке С++ для систем информационной безопасности. Таганрог: Южный федеральный университет, 2020. 152 с.

Семенчев Григорий Владимирович, студент, montekek011@mail.ru, Россия, Тула, Тульский Государственный Университет,

Борзенкова Светлана Юрьевна, канд. техн. наук, доцент, tehnol@rambler.ru, Россия, Тула, Тульский Государственный Университет

THREAT TO INFORMATION SECURITY WHEN THE COMPILER OPTIMIZES THE MEMSET FUNCTION

IN THE C++ PROGRAMMING LANGUAGE

G.V. Semenchev, S.Yu. Borzenkova

The article investigates the security threat that occurs due to the compiler's optimization of memset function while developing software in C+ + programming language.

Key words: optimization, compiler, threat, development.

Semenchev Grigory Vladimirovich, student, montekekOl 1 @mail. ru, Russia, Tula, Tula State University,

Borzenkova Svetlana Yurievna, candidate of technical sciences, docent, tehnol@rambler.ru, Russia, Tula, Tula State University

УДК 624.19

DOI: 10.24412/2071-6168-2023-3-526-531

РАСЧЁТ ЭСКАЛАТОРНЫХ ТОННЕЛЕЙ В.В. Мельник, Р.А. Соловьев, С.В. Рябков, Д.А. Соловьев

Проходка наклонного тоннеля осуществляется с созданием ледогрунтового ограждения, которое заходит на 2-3м в глины для создания герметичного контура. Это образует вокруг тоннеля сначала область замороженных, укрепленных грунтов, затем, после возведения обделки и разморозки массива, грунты теряют прочностные и деформационные характеристики, происходит их усадка. Оттаивание грунтов служит причиной повышенной осадки верхней части наклонных ходов, превышающих строительный подъем, иногда на 10-15см. Осадка увеличивается за счет давления массивных вестибюлей, находящихся на поверхности. Описаны особенности работы чугунных эскалаторных тоннелей в условиях Санкт-Петербурга, сооружаемых с заморозкой грунтового массива, предложены рекомендации по методам решения задачи методами механики сплошной среды.

Ключевые слова: обделки эскалаторных тоннелей, чугун, НДС, метод механики сплошных

сред.

В текущих задачах возникает необходимость в развитии, актуализации и верификации современных расчетных комплексов, разработанных в отечественных институтах. Для сохранения конкурентоспособности и актуальности необходимо адаптировать отечественные расчетные комплексы для решения большей области геотехнических задач. На протяжении длительного периода времени Тульский государственный университет применял и адаптировал расчетные комплексы, разработанные Н.С. Булычевым, Н.Н. Фотиевой, А.С. Самалем, С.В. Анциферовым, П.В. Деевым для решения геотехнических задач в области горной проходки и строительства [1 - 5]. Имеются некоторые очевидные преимущества в программах, которые реализуют механику сплошной среды, такие как: отсутствие погрешности из-за разбиения сетки конечных элементов, отсутствие излишней погрешности, возможность расчета многослойных обделок, быстрый расчет моделей, и, как следствие, возможность подбора более экономичной конструкции путем большого количества расчетов или прикрепления программы оптимизации.

Конструкторским отделом ОАО «Ленметрогипротранс» обследован ряд чугунных обделок наклонных ходов, залегающих в условиях Санкт-Петербурга. Обследованные конструкции прослужили от 30 до 50 лет в слабых грунтах в сложных условиях. Обнаружен ряд факторов, которые могут приблизить теорию к практике. Схематический геологический разрез по оси наклонного хода приведен на рис. 1. Тоннель пересекают слабые грунты с модулем деформации от 7 до 15 МПа, представленные текучими глинами и суглинками, заходит в область перемятых глин, затем в область неповрежденных кембрийских глин.

Пусть угол наклонного тоннеля к горизонтали составляет 30°. Найдем напряжения, перпендикулярные площадке m-n при одноосном растяжении (рис. 2).

P • P • ^ F • P cos а о о cos2 а

О = —' Ра = —' — =-' Ра = °1COSа; о, =alC°s а

—0 —а C0S а —0

Проходка наклонного тоннеля осуществляется с созданием ледогрунтового ограждения, которое заходит на 2-3м в перемятые глины для создания герметичного контура. Это образует вокруг тоннеля сначала область замороженных, укрепленных грунтов, затем, после возведения обделки и разморозки

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.