Skip to content

Commit b8b9bda

Browse files
authored
Merge branch 'main' into main
2 parents cd77ed5 + b879e29 commit b8b9bda

File tree

10 files changed

+359
-3
lines changed

10 files changed

+359
-3
lines changed

.github/workflows/ci.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,18 @@ jobs:
178178
run: |
179179
git-secrets --register-aws
180180
git-secrets --scan
181+
proof_ci:
182+
runs-on: cbmc_ubuntu-latest_16-core
183+
steps:
184+
- name: Set up CBMC runner
185+
uses: FreeRTOS/CI-CD-Github-Actions/set_up_cbmc_runner@main
186+
with:
187+
cbmc_version: "5.61.0"
188+
- run: |
189+
git submodule update --init --checkout --recursive
190+
sudo apt-get update
191+
sudo apt-get install --yes --no-install-recommends gcc-multilib
192+
- name: Run CBMC
193+
uses: FreeRTOS/CI-CD-Github-Actions/run_cbmc@main
194+
with:
195+
proofs_dir: test/cbmc/proofs
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Release Candidate Automation
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
commit_id:
7+
description: 'Commit ID to tag'
8+
required: true
9+
version_number:
10+
description: 'Release Version Number (Eg, v1.0.0-rc1)'
11+
required: true
12+
13+
jobs:
14+
tag-commit:
15+
name: Tag commit
16+
runs-on: ubuntu-latest
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v2
20+
with:
21+
ref: ${{ github.event.inputs.commit_id }}
22+
- name: Configure git identity
23+
run: |
24+
git config --global user.name ${{ github.actor }}
25+
git config --global user.email ${{ github.actor }}@users.noreply.github.com
26+
- name: Tag Commit and Push to Remote
27+
run: |
28+
git tag ${{ github.event.inputs.version_number }} -a -m "FreeRTOS-Plus-TCP Library ${{ github.event.inputs.version_number }}"
29+
git push origin --tags
30+
- name: Verify tag on remote
31+
run: |
32+
git tag -d ${{ github.event.inputs.version_number }}
33+
git remote update
34+
git checkout tags/${{ github.event.inputs.version_number }}
35+
git diff ${{ github.event.inputs.commit_id }} tags/${{ github.event.inputs.version_number }}

source/FreeRTOS_ARP.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,8 @@ BaseType_t xCheckRequiresARPResolution( const NetworkBufferDescriptor_t * pxNetw
413413
const IPPacket_t * pxIPPacket = ( ( IPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer );
414414
const IPHeader_t * pxIPHeader = &( pxIPPacket->xIPHeader );
415415

416-
if( ( pxIPHeader->ulSourceIPAddress & xNetworkAddressing.ulNetMask ) == ( *ipLOCAL_IP_ADDRESS_POINTER & xNetworkAddressing.ulNetMask ) )
416+
if( ( ( pxIPHeader->ulSourceIPAddress & xNetworkAddressing.ulNetMask ) == ( *ipLOCAL_IP_ADDRESS_POINTER & xNetworkAddressing.ulNetMask ) ) &&
417+
( ( pxIPHeader->ulDestinationIPAddress & ipLOOPBACK_NETMASK ) != ( ipLOOPBACK_ADDRESS & ipLOOPBACK_NETMASK ) ) )
417418
{
418419
/* If the IP is on the same subnet and we do not have an ARP entry already,
419420
* then we should send out ARP for finding the MAC address. */
@@ -726,7 +727,8 @@ eARPLookupResult_t eARPGetCacheEntry( uint32_t * pulIPAddress,
726727
* can be done. */
727728
eReturn = eCantSendPacket;
728729
}
729-
else if( *ipLOCAL_IP_ADDRESS_POINTER == *pulIPAddress )
730+
else if( ( *ipLOCAL_IP_ADDRESS_POINTER == *pulIPAddress ) ||
731+
( ( *pulIPAddress & ipLOOPBACK_NETMASK ) == ( ipLOOPBACK_ADDRESS & ipLOOPBACK_NETMASK ) ) )
730732
{
731733
/* The address of this device. May be useful for the loopback device. */
732734
eReturn = eARPCacheHit;

source/FreeRTOS_IP.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,6 +1446,8 @@ static eFrameProcessingResult_t prvAllowIPPacket( const IPPacket_t * const pxIPP
14461446
else if( ( ulDestinationIPAddress != *ipLOCAL_IP_ADDRESS_POINTER ) &&
14471447
/* Is it the global broadcast address 255.255.255.255 ? */
14481448
( ulDestinationIPAddress != ipBROADCAST_IP_ADDRESS ) &&
1449+
/* Is it a loopback address ? */
1450+
( ( ulDestinationIPAddress & ipLOOPBACK_NETMASK ) != ( ipLOOPBACK_ADDRESS & ipLOOPBACK_NETMASK ) ) &&
14491451
/* Is it a specific broadcast address 192.168.1.255 ? */
14501452
( ulDestinationIPAddress != xNetworkAddressing.ulBroadcastAddress ) &&
14511453
#if ( ipconfigUSE_LLMNR == 1 )

source/include/FreeRTOS_IP_Private.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,10 @@ extern const BaseType_t xBufferAllocFixedSize;
387387
* rather than duplicated in its own variable. */
388388
#define ipLOCAL_MAC_ADDRESS ( xDefaultPartUDPPacketHeader.ucBytes )
389389

390+
/* The loopback address block is defined as part of rfc5735 */
391+
#define ipLOOPBACK_ADDRESS ( FreeRTOS_inet_addr_quick( 127, 0, 0, 0 ) )
392+
#define ipLOOPBACK_NETMASK ( FreeRTOS_inet_addr_quick( 255, 0, 0, 0 ) )
393+
390394
/* ICMP packets are sent using the same function as UDP packets. The port
391395
* number is used to distinguish between the two, as 0 is an invalid UDP port. */
392396
#define ipPACKET_CONTAINS_ICMP_DATA ( 0 )
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4+
# SPDX-License-Identifier: MIT-0
5+
6+
7+
import logging
8+
import pathlib
9+
import shutil
10+
import subprocess
11+
12+
13+
_TOOLS = [
14+
"cadical",
15+
"cbmc",
16+
"cbmc-viewer",
17+
"cbmc-starter-kit-update",
18+
"kissat",
19+
"litani",
20+
]
21+
22+
23+
def _format_versions(table):
24+
lines = [
25+
"<table>",
26+
'<tr><td colspan="2" style="font-weight: bold">Tool Versions</td></tr>',
27+
]
28+
for tool, version in table.items():
29+
if version:
30+
v_str = f'<code><pre style="margin: 0">{version}</pre></code>'
31+
else:
32+
v_str = '<em>not found</em>'
33+
lines.append(
34+
f'<tr><td style="font-weight: bold; padding-right: 1em; '
35+
f'text-align: right;">{tool}:</td>'
36+
f'<td>{v_str}</td></tr>')
37+
lines.append("</table>")
38+
return "\n".join(lines)
39+
40+
41+
def _get_tool_versions():
42+
ret = {}
43+
for tool in _TOOLS:
44+
err = f"Could not determine version of {tool}: "
45+
ret[tool] = None
46+
if not shutil.which(tool):
47+
logging.error("%s'%s' not found on $PATH", err, tool)
48+
continue
49+
cmd = [tool, "--version"]
50+
proc = subprocess.Popen(cmd, text=True, stdout=subprocess.PIPE)
51+
try:
52+
out, _ = proc.communicate(timeout=10)
53+
except subprocess.TimeoutExpired:
54+
logging.error("%s'%s --version' timed out", err, tool)
55+
continue
56+
if proc.returncode:
57+
logging.error(
58+
"%s'%s --version' returned %s", err, tool, str(proc.returncode))
59+
continue
60+
ret[tool] = out.strip()
61+
return ret
62+
63+
64+
def main():
65+
exe_name = pathlib.Path(__file__).name
66+
logging.basicConfig(format=f"{exe_name}: %(message)s")
67+
68+
table = _get_tool_versions()
69+
out = _format_versions(table)
70+
print(out)
71+
72+
73+
if __name__ == "__main__":
74+
main()

test/cbmc/proofs/lib/summarize.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: MIT-0
3+
4+
import argparse
5+
import json
6+
import logging
7+
import os
8+
import sys
9+
10+
11+
DESCRIPTION = """Print 2 tables in GitHub-flavored Markdown that summarize
12+
an execution of CBMC proofs."""
13+
14+
15+
def get_args():
16+
"""Parse arguments for summarize script."""
17+
parser = argparse.ArgumentParser(description=DESCRIPTION)
18+
for arg in [{
19+
"flags": ["--run-file"],
20+
"help": "path to the Litani run.json file",
21+
"required": True,
22+
}]:
23+
flags = arg.pop("flags")
24+
parser.add_argument(*flags, **arg)
25+
return parser.parse_args()
26+
27+
28+
def _get_max_length_per_column_list(data):
29+
ret = [len(item) + 1 for item in data[0]]
30+
for row in data[1:]:
31+
for idx, item in enumerate(row):
32+
ret[idx] = max(ret[idx], len(item) + 1)
33+
return ret
34+
35+
36+
def _get_table_header_separator(max_length_per_column_list):
37+
line_sep = ""
38+
for max_length_of_word_in_col in max_length_per_column_list:
39+
line_sep += "|" + "-" * (max_length_of_word_in_col + 1)
40+
line_sep += "|\n"
41+
return line_sep
42+
43+
44+
def _get_entries(max_length_per_column_list, row_data):
45+
entries = []
46+
for row in row_data:
47+
entry = ""
48+
for idx, word in enumerate(row):
49+
max_length_of_word_in_col = max_length_per_column_list[idx]
50+
space_formatted_word = (max_length_of_word_in_col - len(word)) * " "
51+
entry += "| " + word + space_formatted_word
52+
entry += "|\n"
53+
entries.append(entry)
54+
return entries
55+
56+
57+
def _get_rendered_table(data):
58+
table = []
59+
max_length_per_column_list = _get_max_length_per_column_list(data)
60+
entries = _get_entries(max_length_per_column_list, data)
61+
for idx, entry in enumerate(entries):
62+
if idx == 1:
63+
line_sep = _get_table_header_separator(max_length_per_column_list)
64+
table.append(line_sep)
65+
table.append(entry)
66+
table.append("\n")
67+
return "".join(table)
68+
69+
70+
def _get_status_and_proof_summaries(run_dict):
71+
"""Parse a dict representing a Litani run and create lists summarizing the
72+
proof results.
73+
74+
Parameters
75+
----------
76+
run_dict
77+
A dictionary representing a Litani run.
78+
79+
80+
Returns
81+
-------
82+
A list of 2 lists.
83+
The first sub-list maps a status to the number of proofs with that status.
84+
The second sub-list maps each proof to its status.
85+
"""
86+
count_statuses = {}
87+
proofs = [["Proof", "Status"]]
88+
for proof_pipeline in run_dict["pipelines"]:
89+
status_pretty_name = proof_pipeline["status"].title().replace("_", " ")
90+
try:
91+
count_statuses[status_pretty_name] += 1
92+
except KeyError:
93+
count_statuses[status_pretty_name] = 1
94+
if proof_pipeline["name"] == "print_tool_versions":
95+
continue
96+
proofs.append([proof_pipeline["name"], status_pretty_name])
97+
statuses = [["Status", "Count"]]
98+
for status, count in count_statuses.items():
99+
statuses.append([status, str(count)])
100+
return [statuses, proofs]
101+
102+
103+
def print_proof_results(out_file):
104+
"""
105+
Print 2 strings that summarize the proof results.
106+
When printing, each string will render as a GitHub flavored Markdown table.
107+
"""
108+
output = "## Summary of CBMC proof results\n\n"
109+
with open(out_file, encoding='utf-8') as run_json:
110+
run_dict = json.load(run_json)
111+
status_table, proof_table = _get_status_and_proof_summaries(run_dict)
112+
for summary in (status_table, proof_table):
113+
output += _get_rendered_table(summary)
114+
115+
print(output)
116+
sys.stdout.flush()
117+
118+
github_summary_file = os.getenv("GITHUB_STEP_SUMMARY")
119+
if github_summary_file:
120+
with open(github_summary_file, "a") as handle:
121+
print(output, file=handle)
122+
handle.flush()
123+
else:
124+
logging.warning(
125+
"$GITHUB_STEP_SUMMARY not set, not writing summary file")
126+
127+
msg = (
128+
"Click the 'Summary' button to view a Markdown table "
129+
"summarizing all proof results")
130+
if run_dict["status"] != "success":
131+
logging.error("Not all proofs passed.")
132+
logging.error(msg)
133+
sys.exit(1)
134+
logging.info(msg)
135+
136+
137+
if __name__ == '__main__':
138+
args = get_args()
139+
logging.basicConfig(format="%(levelname)s: %(message)s")
140+
try:
141+
print_proof_results(args.run_file)
142+
except Exception as ex: # pylint: disable=broad-except
143+
logging.critical("Could not print results. Exception: %s", str(ex))

test/cbmc/proofs/run-cbmc-proofs.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,13 @@ def main():
301301
if not args.no_standalone:
302302
run_build(args.parallel_jobs)
303303

304+
out_sym = pathlib.Path("/tmp")/"litani"/"runs"/"latest"
305+
out_dir = out_sym.resolve()
306+
307+
local_copy = pathlib.Path("output")/"latest"
308+
local_copy.parent.mkdir(exist_ok=True)
309+
local_copy.symlink_to(out_dir)
310+
304311

305312
if __name__ == "__main__":
306313
main()
307-

test/unit-test/FreeRTOS_ARP/FreeRTOS_ARP_utest.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,29 @@ void test_xCheckRequiresARPResolution_OnLocalNetwork_InCache( void )
945945
TEST_ASSERT_EQUAL( pdFALSE, xResult );
946946
}
947947

948+
void test_xCheckRequiresARPResolution_OnLocalNetwork_Loopback( void )
949+
{
950+
NetworkBufferDescriptor_t xNetworkBuffer, * pxNetworkBuffer;
951+
uint8_t ucEthernetBuffer[ ipconfigNETWORK_MTU ];
952+
BaseType_t xResult;
953+
954+
pxNetworkBuffer = &xNetworkBuffer;
955+
pxNetworkBuffer->pucEthernetBuffer = ucEthernetBuffer;
956+
957+
IPPacket_t * pxIPPacket = ( ( IPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer );
958+
IPHeader_t * pxIPHeader = &( pxIPPacket->xIPHeader );
959+
960+
*ipLOCAL_IP_ADDRESS_POINTER = ipLOOPBACK_ADDRESS;
961+
962+
/* Make sure there is a match on source IP and loopback address */
963+
pxIPHeader->ulDestinationIPAddress = *ipLOCAL_IP_ADDRESS_POINTER;
964+
pxIPHeader->ulSourceIPAddress = *ipLOCAL_IP_ADDRESS_POINTER & xNetworkAddressing.ulNetMask;
965+
966+
xResult = xCheckRequiresARPResolution( pxNetworkBuffer );
967+
968+
TEST_ASSERT_EQUAL( pdFALSE, xResult );
969+
}
970+
948971

949972
void test_ulARPRemoveCacheEntryByMac_NoMatch( void )
950973
{
@@ -1322,6 +1345,23 @@ void test_eARPGetCacheEntry_IPMatchesOtherBroadcastAddr( void )
13221345
/* =================================================== */
13231346
}
13241347

1348+
void test_eARPGetCacheEntry_IPMatchesLoopbackAddr( void )
1349+
{
1350+
uint32_t ulIPAddress;
1351+
MACAddress_t xMACAddress;
1352+
eARPLookupResult_t eResult;
1353+
uint32_t ulSavedGatewayAddress;
1354+
1355+
/* =================================================== */
1356+
ulIPAddress = ipLOOPBACK_ADDRESS;
1357+
/* Not worried about what these functions do. */
1358+
xIsIPv4Multicast_ExpectAndReturn( ulIPAddress, 0UL );
1359+
eResult = eARPGetCacheEntry( &ulIPAddress, &xMACAddress );
1360+
TEST_ASSERT_EQUAL_MESSAGE( eARPCacheHit, eResult, "Test 3" );
1361+
TEST_ASSERT_EQUAL_MEMORY_MESSAGE( &ipLOCAL_MAC_ADDRESS, &xMACAddress, sizeof( xMACAddress ), "Test 3" );
1362+
/* =================================================== */
1363+
}
1364+
13251365
void test_eARPGetCacheEntry_LocalIPIsZero( void )
13261366
{
13271367
uint32_t ulIPAddress;

0 commit comments

Comments
 (0)