; $next_address gives jump to
; address_a
:address_g
END-MESSAGE (0, 0, 0, 0, 0, 0, 0)
The output of the code is 0x0001 0102 0203 0304 0405 0506 0707 0708
0808 0909, and a total of 131 UDVM cycles are used.
2.15. State Creation
This section gives assembly code to test the STATE-CREATE and STATE-
FREE instructions. The code is designed to test that the following
boundary cases have been correctly implemented:
1. An item of state is created that duplicates an existing state
item.
2. An item of state is freed when the state has not been created.
3. An item of state is created and then freed by the same message.
4. The STATE-FREE instruction frees a state item by sending fewer
bytes of the state_identifier than the minimum_access_length.
5. The STATE-FREE instruction has partial_identifier_length operand
shorter than 6 or longer than 20.
6. The STATE-FREE instruction specifies a partial_identifier that
matches with two state items in the compartment.
7. The bytes of the identifier are written to the position specified
in the STATE-FREE instruction after the STATE-FREE instruction
has been run (and before END-MESSAGE).
at (64)
:byte_copy_left pad (2)
:byte_copy_right pad (2)
:states pad (1)
:states_lsb pad (1)
:min_len pad (1)
:min_len_lsb pad (1)
:state_identifier pad (20)
set (state_length, 10)
at (127)
:decompression_failure
at (128)
INPUT-BYTES (1, states_lsb, decompression_failure)
:test_one
LSHIFT ($states, 11)
COMPARE ($states, 32768, test_two, create_state_a2, create_state_a2)
:create_state_a2
STATE-CREATE (state_length, state_address2, 0, 20, 0)
:test_two
LSHIFT ($states, 1)
COMPARE ($states, 32768, test_three, create_state_a, create_state_a)
:create_state_a
STATE-CREATE (state_length, state_address, 0, 20, 0)
:test_three
LSHIFT ($states, 1)
COMPARE ($states, 32768, test_four, free_state, free_state)
:free_state
INPUT-BYTES (1, min_len_lsb, decompression_failure)
STATE-FREE (state_identifier, $min_len)
COPY (identifier1, $min_len, state_identifier)
:test_four
LSHIFT ($states, 1)
COMPARE ($states, 32768, test_five, free_state2, free_state2)
:free_state2
STATE-FREE (identifier1, 6)
:test_five
LSHIFT ($states, 1)
COMPARE ($states, 32768, end, create_state_b, create_state_b)
:create_state_b
END-MESSAGE (0, 0, state_length, state_address, 0, 20, 0)
:end
END-MESSAGE (0, 0, 0, 0, 0, 0, 0)
:identifier1
byte (67, 122, 232, 10, 15, 220, 30, 106, 135, 193, 182, 42, 118,
118, 185, 115, 49, 140, 14, 245)
at (256)
:state_address
byte (192, 204, 63, 238, 121, 188, 252, 143, 209, 8)
:state_address2
byte (101, 232, 3, 82, 238, 41, 119, 23, 223, 87)
Upon reaching the END-MESSAGE instruction, the UDVM does not output
any decompressed data, but instead may make one or more state
creation or state free requests to the state handler. Assuming that
the application does not veto the state creation request (and that
sufficient state memory is available) the code results in 0, 1, or 2
state items being present in the compartment.
The following table lists ten different compressed messages, the
states created and freed by each, the number of states left after
each message, and the number of UDVM cycles used. There are 3 state
creation instructions:
create state_a, which has hash identifier1
create state_b (in END-MESSAGE), which is identical to state_a
create state_a2, which has a different identifier, but the first 6
bytes are the same as those of identifier1.
Message: Effect: # state items: #cycles:
0x01 create state_b 1 23
0x02 free (id1, 6) = state_b 0 14
0x03 free (id1, 6) = state_b; create state_b 1 24
0x0405 free (id1, 5) Decompression failure
0x0415 free (id1, 21) Decompression failure
0x0406 free (id1, 6) = state_b 0 23
0x09 create state_a; create state_b 1 34
0x1e06 create state_a2; create state_a;
free (id1, 6) = matches both so no free;
free (id1, 6) = matches both so no free; 2 46
0x1e07 create state_a2; create state_a;
free (id1, 7) = state_a;
free (id1, 6) = state_a2 0 47
0x1e14 create state_a2; create state_a;
free (id1, 20) = state_a;
free (id1, 6) = state_a2 0 60
2.16. STATE-ACCESS
This section gives assembly code to test the STATE-ACCESS
instruction. The code is designed to test that the following
boundary cases have been correctly implemented:
1. A subset of the bytes contained in a state item is copied to the
UDVM memory.
2. Bytes are copied from beyond the end of the state value.
3. The state_instruction operand is set to 0.
4. The state cannot be accessed because the partial state identifier
is too short.
5. The state identifier is overwritten by the state item being
accessed.
The following bytecode needs to be run first to set up the state for
the rest of the test.
at (128)
END-MESSAGE (0, 0, state_length, state_start, 0, 20, 0)
; The bytes between state_start and state_end are derived from
; translation of the following mnemonic code:
;
; at (512)
; OUTPUT (data, 4)
; END-MESSAGE (0,0,0,0,0,0,0)
; :data
; byte (116, 101, 115, 116)
at (512)
:state_start
byte (34, 162, 12,4, 35, 0, 0, 0, 0, 0, 0, 0, 116, 101, 115, 116)
:state_end
set (state_length, (state_end - state_start))
This is the bytecode for the rest of the test.
at (64)
:byte_copy_left pad (2)
:byte_copy_right pad (2)
:type pad (1)
:type_lsb pad (1)
:state_value pad (4)
at (127)
:decompression_failure
at (128)
INPUT-BYTES (1, type_lsb, decompression_failure)
COMPARE ($type, 1, execute_state, extract_state, error_conditions)
:execute_state
STATE-ACCESS (state_identifier, 20, 0, 0, 0, 512)
:extract_state
STATE-ACCESS (state_identifier, 20, 12, 4, state_value, 0)
OUTPUT (state_value, 4)
JUMP (end)
:error_conditions
COMPARE ($type, 3, state_not_found, id_too_short, state_too_short)
:state_not_found
STATE-ACCESS (128, 20, 0, 0, 0, 0)
JUMP (end)
:id_too_short
STATE-ACCESS (state_identifier, 19, 6, 4, state_value, 0)
JUMP (end)
:state_too_short
STATE-ACCESS (state_identifier, 20, 12, 5, state_value, 0)
JUMP (end)
at (484)
:end
END-MESSAGE (0, 0, 0, 0, 0, 0, 0)
at (512)
:state_identifier
byte (0x5d, 0xf8, 0xbc, 0x3e, 0x20, 0x93, 0xb5, 0xab, 0xe1, 0xf1,
0x70, 0x13, 0x42, 0x4c, 0xe7, 0xfe, 0x05, 0xe0, 0x69, 0x39)
If the compressed message is 0x00, then the output of the code is
0x7465 7374, and a total of 26 UDVM cycles are used. If the
compressed message is 0x01, then the output of the code is also
0x7465 7374 but in this case using a total of 15 UDVM cycles. If the
compressed message is 0x02, 0x03, or 0x04, then decompression failure
occurs.
3. Torture Tests for Dispatcher
The following sections give code to test the various functions of the
SigComp dispatcher.
3.1. Useful Values
This section gives assembly code to test that the SigComp "Useful
Values" are correctly initialized in the UDVM memory. It also tests
that the UDVM is correctly terminated if the bytecode uses too many
UDVM cycles or tries to write beyond the end of the available memory.
The code tests that the following boundary cases have been correctly
implemented:
1. The bytecode uses exactly as many UDVM cycles as are available
(in which case no problems should arise) or one cycle too many
(in which case decompression failure should occur). A liberal
implementation could allow more cycles to be used than are
strictly available, in which case decompression failure will not
occur. This is an implementation choice. If this choice is
made, the implementer must be sure that the cycles are checked
eventually and that decompression failure does occur when
bytecode uses an excessive number of cycles. This is tested in
Section 3.2.
2. The bytecode writes to the highest memory address available (in
which case no problems should arise) or to the memory address
immediately following the highest available address (in which
case decompression failure must occur).
:udvm_memory_size pad (2)
:cycles_per_bit pad (2)
:sigcomp_version pad (2)
:partial_state_id_length pad (2)
:state_length pad (2)
at (64)
:byte_copy_left pad (2)
:byte_copy_right pad (2)
:remaining_cycles pad (2)
:check_memory pad (1)
:check_memory_lsb pad (1)
:check_cycles pad (1)
:check_cycles_lsb pad (1)
at (127)
:decompression_failure
at (128)
; Set up a 1-byte buffer
LOAD (byte_copy_left, 32)
LOAD (byte_copy_right, 33)
:test_version
; Input a byte containing the version of SigComp being run
INPUT-BYTES (1, check_memory_lsb, decompression_failure)
COMPARE ($sigcomp_version, $check_memory, decompression_failure,
test_state_access, decompression_failure)
:test_state_access
COMPARE ($partial_state_id_length, 0, decompression_failure,
test_length_equals_zero, test_state_length)
:test_length_equals_zero
; No state was accessed so state_length
; should be zero (first message)
COMPARE ($state_length, 0, decompression_failure, end,
decompression_failure)
:test_state_length
; State was accessed so state_length
; should be 960
COMPARE ($state_length, 960, decompression_failure, test_udvm_memory,
decompression_failure)
:test_udvm_memory
; Copy one byte to
; udvm_memory_size + input - 1
; Succeed when input byte is 0x00
; Fail when input byte is 0x01
INPUT-BYTES (1, check_memory_lsb, decompression_failure)
ADD ($check_memory, $udvm_memory_size)
SUBTRACT ($check_memory, 1)
COPY (32, 1, $check_memory)
:test_udvm_cycles
INPUT-BYTES (1, check_cycles_lsb, decompression_failure)
; Work out the total number of cycles available to the UDVM
; total_UDVM_cycles = cycles_per_bit * (8 * message_size + 1000)
;
; = cycles_per_bit * (8 * (partial_state_id_length + 3) + 1000)
LOAD (remaining_cycles, $partial_state_id_length)
ADD ($remaining_cycles, 3)
MULTIPLY ($remaining_cycles, 8)
ADD ($remaining_cycles, 1000)
MULTIPLY ($remaining_cycles, $cycles_per_bit)
ADD ($remaining_cycles, $check_cycles)
set (cycles_used_by_bytecode, 856)
SUBTRACT ($remaining_cycles, cycles_used_by_bytecode)
COPY (32, $remaining_cycles, 32)
; Copy to use up all cycles available + input byte
; Succeeds when input byte = 0x00
; Fail when input byte = 0x01
:end
; Create 960 bytes of state for future
; reference
END-MESSAGE (0, 0, 960, 64, 128, 6, 0)
The bytecode must be executed a total of four times in order to fully
test the SigComp Useful Values. In the first case, the bytecode is
uploaded as part of the SigComp message with a 1-byte compressed
message corresponding to the version of SigComp being run. This
causes the UDVM to request creation of a new state item and uses a
total of 968 UDVM cycles.
Subsequent tests access this state by uploading the state identifier
as part of the SigComp message. Note that the SigComp message should
not contain a returned feedback item (as this would cause the
bytecode to calculate the total number of available UDVM cycles
incorrectly).
A 3-byte compressed message is required for the second and subsequent
cases, the first byte of which is the version of SigComp in use,
0xnn. If the message is 0xnn0000, then the UDVM should successfully
terminate using exactly the number of available UDVM cycles.
However, if the message is 0xnn0001, then the UDVM should use too
many cycles and hence terminate with decompression failure.
Furthermore, if the message is 0xnn0100, then decompression failure
must occur because the UDVM attempts to write beyond its available
memory.
3.2. Cycles Checking
As discussed in Section 3.1, it is possible to write an
implementation that takes a liberal approach to checking the cycles
used and allows some extra cycles. The implementer must be sure that
decompression failure does not occur too early and that in the case
of excessive use of cycles, decompression failure does eventually
occur. This test checks that:
1. Decompression failure occurs eventually when there is an infinite
loop.
at (64)
:byte_copy_left pad (2)
:byte_copy_right pad (2)
:value pad (2)
:copy_next pad (2)
at(128)
MULTILOAD (byte_copy_left, 4, 32, 41, 0, 34)
; Set up a 10-byte buffer
; Set the value to copy
; Copy it 100 times,
; output the value,
; increment the counter
:loop
COPY (value, 2, $byte_copy_left)
COPY-OFFSET (2, 100, $copy_next)
OUTPUT (value, 2)
ADD ($value, 1)
JUMP (loop)
If the cycles are counted exactly and cycles per bit (cpb) = 16, then
decompression failure will occur at COPY-OFFSET when value = 180 =
0xB4. If cpb = 32, then decompression failure will occur when value
= 361 = 0x0169. If they are not counted exactly, then decompression
failure MUST occur eventually.
3.3. Message-based Transport
This section provides a set of messages to test the SigComp header
over a message-based transport such as UDP. The messages test that
the following boundary cases have been correctly implemented:
1. The UDVM bytecode is copied to different areas of the UDVM
memory.
2. The decompression memory size is set to an incorrect value.
3. The SigComp message is too short.
4. The destination address is invalid.
The basic version of the code used in the test is given below. Note
that the code is designed to calculate the decompression memory size
based on the Useful Values provided to the UDVM:
:udvm_memory_size pad (2)
:cycles_per_bit pad (2)
:sigcomp_version pad (2)
:partial_state_id_length pad (2)
:state_length pad (2)
at (128)
:code_start
; udvm_memory_size for message-based transport
; = DMS - total_message_size
ADD ($udvm_memory_size, total_message_size)
OUTPUT (udvm_memory_size, 2)
END-MESSAGE (0, 0, 0, 0, 0, 0, 1)
:code_end
set (header_size, 3)
set (code_size, (code_end - code_start))
set (total_message_size, (header_size + code_size))
A number of complete SigComp messages are given below, each
containing some or all of the above code. In each case, it is
indicated whether the message will successfully output the
decompression memory size or whether it will cause a decompression
failure to occur (together with the reason for the failure):
SigComp message: Effect:
0xf8 Fails (message too short)
0xf800 Fails (message too short)
0xf800 e106 0011 2200 0223 Outputs the decompression_memory_size
0x0000 0000 0000 01
0xf800 f106 0011 2200 0223 Fails (message too short)
0x0000 0000 0000 01
0xf800 e006 0011 2200 0223 Fails (invalid destination address)
0x0000 0000 0000 01
0xf800 ee06 0011 2200 0223 Outputs the decompression_memory_size
0x0000 0000 0000 01
The messages should be decompressed in the order given to check that
an error in one message does not interfere with the successful
decompression of subsequent messages.
The two messages that successfully decompress each use a total of 5
UDVM cycles.
3.4. Stream-based Transport
This section provides a byte stream to test the SigComp header and
delimiters over a stream-based transport such as TCP. The byte
stream tests all of the boundary cases covered in Section 3.2, as
well as the following cases specific to stream-based transports:
1. Quoted bytes are used by the record marking scheme.
2. Multiple delimiters are used between the same pair of messages.
3. Unnecessary delimiters are included at the start of the stream.
The basic version of the code used in the test is given below. Note
that the code is designed to calculate the decompression memory size
based on the Useful Values provided to the UDVM:
:udvm_memory_size pad (2)
:cycles_per_bit pad (2)
:sigcomp_version pad (2)
:partial_state_id_length pad (2)
:state_length pad (2)
at (128)
; udvm_memory_size for stream based transport = DMS / 2
MULTIPLY ($udvm_memory_size, 2)
OUTPUT (udvm_memory_size, 2)
OUTPUT (test_record_marking, 5)
END-MESSAGE (0, 0, 0, 0, 0, 0, 0)
:test_record_marking
byte (255, 255, 255, 255, 255)
The above assembly code has been compiled and used to generate the
following byte stream:
0xffff f801 7108 0002 2200 0222 a092 0523 0000 0000 0000 00ff 00ff
0x03ff ffff ffff ffff f801 7e08 0002 2200 0222 a3d2 0523 0000 0000
0x0000 00ff 04ff ffff ffff ffff ffff ff
Note that this byte stream can be divided into five distinct portions
(two SigComp messages and three sets of delimiters) as illustrated
below:
Portion of byte stream: Meaning:
0xffff Delimiter
0xf801 7108 0002 2200 0222 a092 0523 First message
0x0000 0000 0000 00ff 00ff 03ff ffff
0xffff ffff Delimiter
0xf801 7e08 0002 2200 0222 a3d2 0523 Second message
0x0000 0000 0000 00ff 04ff ffff ff
0xffff ffff ffff Delimiter
When the complete byte stream is supplied to the decompressor
dispatcher, the record marking scheme must use the delimiters to
partition the stream into two distinct SigComp messages. Both of
these messages successfully output the decompression memory size (as
a 2-byte value), followed by 5 consecutive 0xff bytes to test that
the record marking scheme is working correctly. A total of 11 UDVM
cycles are used in each case.
It must also be checked that the dispatcher can handle the same error
cases as covered in Section 3.2. Each of the following byte streams
should cause a decompression failure to occur for the reason stated:
Byte stream: Reason for failure:
0xf8ff ff Message too short
0xf800 ffff Message too short
0xf801 8108 0002 2200 0222 a092 0523 ffff Message too short
0x0000 0000 0000 00ff 00ff 03ff ffff
0xf801 7008 0002 2200 0222 a092 0523 ffff Invalid destination
0x0000 0000 0000 00ff 04ff ffff ff
3.5. Input Past the End of a Message
This section gives assembly code to test that the implementation
correctly handles input past the end of a SigComp message. The code
is designed to test that the following boundary cases have been
correctly implemented: