1
1
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
# SPDX-License-Identifier: Apache-2.0
3
3
"""Tests scenario for the Firecracker serial console."""
4
+
5
+ import fcntl
6
+ import os
7
+ import termios
4
8
import time
9
+
5
10
from framework .microvm import Serial
6
11
from framework .state_machine import TestState
12
+ import framework .utils as utils
7
13
8
14
9
- class WaitLogin (TestState ):
15
+ class WaitLogin (TestState ): # pylint: disable=too-few-public-methods
10
16
"""Initial state when we wait for the login prompt."""
11
17
12
18
def handle_input (self , serial , input_char ) -> TestState :
@@ -18,7 +24,7 @@ def handle_input(self, serial, input_char) -> TestState:
18
24
return self
19
25
20
26
21
- class WaitPasswordPrompt (TestState ):
27
+ class WaitPasswordPrompt (TestState ): # pylint: disable=too-few-public-methods
22
28
"""Wait for the password prompt to be shown."""
23
29
24
30
def handle_input (self , serial , input_char ) -> TestState :
@@ -32,7 +38,7 @@ def handle_input(self, serial, input_char) -> TestState:
32
38
return self
33
39
34
40
35
- class WaitIDResult (TestState ):
41
+ class WaitIDResult (TestState ): # pylint: disable=too-few-public-methods
36
42
"""Wait for the console to show the result of the 'id' shell command."""
37
43
38
44
def handle_input (self , unused_serial , input_char ) -> TestState :
@@ -42,10 +48,10 @@ def handle_input(self, unused_serial, input_char) -> TestState:
42
48
return self
43
49
44
50
45
- class TestFinished (TestState ):
51
+ class TestFinished (TestState ): # pylint: disable=too-few-public-methods
46
52
"""Test complete and successful."""
47
53
48
- def handle_input (self , unused_serial , input_char ) -> TestState :
54
+ def handle_input (self , unused_serial , _ ) -> TestState :
49
55
"""Return self since the test is about to end."""
50
56
return self
51
57
@@ -68,11 +74,57 @@ def test_serial_console_login(test_microvm_with_ssh):
68
74
69
75
serial = Serial (microvm )
70
76
serial .open ()
71
-
72
- # Set initial state - wait for 'login:' prompt
73
77
current_state = WaitLogin ("login:" )
74
78
75
79
while not isinstance (current_state , TestFinished ):
76
80
output_char = serial .rx_char ()
77
81
current_state = current_state .handle_input (
78
82
serial , output_char )
83
+
84
+
85
+ def get_total_mem_size (pid ):
86
+ """Get total memory usage for a process."""
87
+ cmd = f"pmap { pid } | tail -n 1 | sed 's/^ //' | tr -s ' ' | cut -d' ' -f2"
88
+ rc , stdout , stderr = utils .run_cmd (cmd )
89
+ assert rc == 0
90
+ assert stderr == ""
91
+
92
+ return stdout
93
+
94
+
95
+ def send_bytes (tty , bytes_count , timeout = 60 ):
96
+ """Send data to the terminal."""
97
+ start = time .time ()
98
+ for _ in range (bytes_count ):
99
+ fcntl .ioctl (tty , termios .TIOCSTI , '\n ' )
100
+ current = time .time ()
101
+ if current - start > timeout :
102
+ break
103
+
104
+
105
+ def test_serial_dos (test_microvm_with_ssh ):
106
+ """Test serial console behavior under DoS."""
107
+ microvm = test_microvm_with_ssh
108
+ microvm .jailer .daemonize = False
109
+ microvm .spawn ()
110
+ microvm .memory_events_queue = None
111
+
112
+ # Set up the microVM with 1 vCPU and a serial console.
113
+ microvm .basic_config (vcpu_count = 1 ,
114
+ add_root_device = False ,
115
+ boot_args = 'console=ttyS0 reboot=k panic=1 pci=off' )
116
+ microvm .start ()
117
+
118
+ # Open an fd for firecracker process terminal.
119
+ tty_path = f"/proc/{ microvm .jailer_clone_pid } /fd/0"
120
+ tty_fd = os .open (tty_path , os .O_RDWR )
121
+
122
+ # Check if the total memory size changed.
123
+ before_size = get_total_mem_size (microvm .jailer_clone_pid )
124
+ send_bytes (tty_fd , 100000000 , timeout = 1 )
125
+ after_size = get_total_mem_size (microvm .jailer_clone_pid )
126
+ assert before_size == after_size , "The memory size of the " \
127
+ "Firecracker process " \
128
+ "changed from {} to {}." \
129
+ .format (before_size ,
130
+ after_size )
0 commit comments