Skip to content

Commit d506f57

Browse files
Iulian Barbusandreim
authored andcommitted
serial: add regression test for buffer limit
Signed-off-by: Iulian Barbu <[email protected]>
1 parent ac637fe commit d506f57

File tree

1 file changed

+62
-9
lines changed

1 file changed

+62
-9
lines changed

tests/integration_tests/functional/test_serial_io.py

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,46 @@
11
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
# SPDX-License-Identifier: Apache-2.0
33
"""Tests scenario for the Firecracker serial console."""
4+
5+
import fcntl
6+
import os
7+
import termios
48
import time
9+
from subprocess import run, PIPE
10+
511
from framework.microvm import Serial
612
from framework.state_machine import TestState
713

814

9-
class WaitLogin(TestState):
15+
class WaitLogin(TestState): # pylint: disable=too-few-public-methods
1016
"""Initial state when we wait for the login prompt."""
1117

1218
def handle_input(self, serial, input_char) -> TestState:
1319
"""Handle input and return next state."""
1420
if self.match(input_char):
21+
time.sleep(2)
1522
# Send login username.
1623
serial.tx("root")
24+
time.sleep(2)
1725
return WaitPasswordPrompt("Password:")
1826
return self
1927

2028

21-
class WaitPasswordPrompt(TestState):
29+
class WaitPasswordPrompt(TestState): # pylint: disable=too-few-public-methods
2230
"""Wait for the password prompt to be shown."""
2331

2432
def handle_input(self, serial, input_char) -> TestState:
2533
"""Handle input and return next state."""
2634
if self.match(input_char):
2735
serial.tx("root")
28-
# Wait 1 second for shell
29-
time.sleep(1)
36+
time.sleep(2)
3037
serial.tx("id")
38+
time.sleep(2)
3139
return WaitIDResult("uid=0(root) gid=0(root) groups=0(root)")
3240
return self
3341

3442

35-
class WaitIDResult(TestState):
43+
class WaitIDResult(TestState): # pylint: disable=too-few-public-methods
3644
"""Wait for the console to show the result of the 'id' shell command."""
3745

3846
def handle_input(self, unused_serial, input_char) -> TestState:
@@ -42,10 +50,10 @@ def handle_input(self, unused_serial, input_char) -> TestState:
4250
return self
4351

4452

45-
class TestFinished(TestState):
53+
class TestFinished(TestState): # pylint: disable=too-few-public-methods
4654
"""Test complete and successful."""
4755

48-
def handle_input(self, unused_serial, input_char) -> TestState:
56+
def handle_input(self, unused_serial, _) -> TestState:
4957
"""Return self since the test is about to end."""
5058
return self
5159

@@ -63,16 +71,61 @@ def test_serial_console_login(test_microvm_with_ssh):
6371
# Set up the microVM with 1 vCPU and a serial console.
6472
microvm.basic_config(vcpu_count=1,
6573
boot_args='console=ttyS0 reboot=k panic=1 pci=off')
66-
6774
microvm.start()
6875

6976
serial = Serial(microvm)
7077
serial.open()
7178

7279
# Set initial state - wait for 'login:' prompt
7380
current_state = WaitLogin("login:")
74-
7581
while not isinstance(current_state, TestFinished):
7682
output_char = serial.rx_char()
7783
current_state = current_state.handle_input(
7884
serial, output_char)
85+
86+
87+
def get_total_mem_size(pid):
88+
"""Get total memory usage for a process."""
89+
cmd = f"pmap {pid} | tail -n 1 | sed 's/^ //' | tr -s ' ' | cut -d' ' -f2"
90+
proc = run(cmd, check=True, shell=True, stdout=PIPE)
91+
return proc.stdout.decode()
92+
93+
94+
def send_bytes(tty, bytes_count, timeout=60):
95+
"""Send data to the terminal."""
96+
start = time.time()
97+
for _ in range(bytes_count):
98+
fcntl.ioctl(tty, termios.TIOCSTI, 'a')
99+
current = time.time()
100+
if current - start > timeout:
101+
break
102+
103+
104+
def test_serial_dos(test_microvm_with_ssh):
105+
"""Test serial console behavior when it is under DoS."""
106+
microvm = test_microvm_with_ssh
107+
microvm.jailer.daemonize = False
108+
microvm.spawn()
109+
microvm.memory_events_queue = None
110+
111+
# Set up the microVM with 1 vCPU and a serial console.
112+
microvm.basic_config(vcpu_count=1,
113+
boot_args='console=ttyS0 reboot=k panic=1 pci=off')
114+
microvm.start()
115+
116+
# Get Firecracker process TTY.
117+
tty_path = f"/proc/{microvm.jailer_clone_pid}/fd/0"
118+
tty_fd = os.open(tty_path, os.O_RDWR)
119+
120+
# Check if the total memory size changed.
121+
before_size = get_total_mem_size(microvm.jailer_clone_pid)
122+
# Send 100MB at maximum to the terminal backed by our serial device.
123+
# In practice, we send bytes in a burst for only 1 seconds. This is
124+
# sufficient for exercising serial device buffer capacity.
125+
send_bytes(tty_fd, bytes_count=100000000, timeout=1)
126+
after_size = get_total_mem_size(microvm.jailer_clone_pid)
127+
assert before_size == after_size, "The memory size of the " \
128+
"Firecracker process " \
129+
"changed from {} to {}." \
130+
.format(before_size,
131+
after_size)

0 commit comments

Comments
 (0)