mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-05-27 01:40:05 +08:00
Compare commits
13 Commits
beta
..
release/1.17
| Author | SHA1 | Date | |
|---|---|---|---|
| 68e55cfc2a | |||
| 5389f75577 | |||
| 3b84cdbdbe | |||
| 54ab3158c5 | |||
| fab7cbda8d | |||
| 39b0568ea4 | |||
| 0b6e4687de | |||
| 69a6b9eee6 | |||
| b5b48536a3 | |||
| 3bd06cc02f | |||
| 48335ac3f1 | |||
| 9605d8dce8 | |||
| 3d63672c03 |
@@ -24,6 +24,11 @@ on:
|
||||
description: 'Container tag (e.g. v1.16.0)'
|
||||
required: true
|
||||
type: string
|
||||
build_ref:
|
||||
description: 'Git ref to build from (branch, tag, or SHA). Leave empty to build from the dispatch ref.'
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
deploy_to_registry:
|
||||
description: 'Whether to push built images to the registry'
|
||||
required: false
|
||||
@@ -45,9 +50,10 @@ jobs:
|
||||
meta_tags: ${{ steps.meta.outputs.tags }}
|
||||
meta_labels: ${{ steps.meta.outputs.labels }}
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ github.event.inputs.build_ref || github.ref }}
|
||||
fetch-tags: true
|
||||
submodules: false
|
||||
fetch-depth: 0
|
||||
@@ -64,7 +70,7 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v6
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/PX4/px4-dev
|
||||
@@ -89,22 +95,23 @@ jobs:
|
||||
runner: x64
|
||||
runs-on: [runs-on,"runner=4cpu-linux-${{ matrix.runner }}","image=ubuntu24-full-${{ matrix.runner }}","run-id=${{ github.run_id }}",extras=s3-cache,spot=false]
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ github.event.inputs.build_ref || github.ref }}
|
||||
fetch-tags: true
|
||||
submodules: false
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') || (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_registry) }}
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') || (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_registry) }}
|
||||
with:
|
||||
registry: ghcr.io
|
||||
@@ -112,13 +119,13 @@ jobs:
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
with:
|
||||
driver: docker-container
|
||||
platforms: ${{ matrix.platform }}
|
||||
|
||||
- name: Build and Load Container Image
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v7
|
||||
id: docker
|
||||
with:
|
||||
context: Tools/setup
|
||||
@@ -130,8 +137,8 @@ jobs:
|
||||
load: false
|
||||
push: ${{ startsWith(github.ref, 'refs/tags/') || (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_registry) }}
|
||||
provenance: false
|
||||
cache-from: type=gha,version=1
|
||||
cache-to: type=gha,version=1,mode=max
|
||||
cache-from: type=gha,scope=${{ matrix.arch }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.arch }},ignore-error=true
|
||||
|
||||
deploy:
|
||||
name: Deploy To Registry
|
||||
@@ -140,23 +147,27 @@ jobs:
|
||||
packages: write
|
||||
runs-on: [runs-on,"runner=4cpu-linux-x64","image=ubuntu24-full-x64","run-id=${{ github.run_id }}",extras=s3-cache,spot=false]
|
||||
needs: [build, setup]
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') || (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_registry) }}
|
||||
if: |
|
||||
!cancelled() &&
|
||||
needs.setup.result == 'success' &&
|
||||
(startsWith(github.ref, 'refs/tags/') || (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_registry == 'true'))
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ github.event.inputs.build_ref || github.ref }}
|
||||
fetch-tags: true
|
||||
submodules: false
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -164,10 +175,10 @@ jobs:
|
||||
|
||||
- name: Verify Images Exist Before Creating Manifest
|
||||
run: |
|
||||
docker manifest inspect px4io/px4-dev:${{ needs.setup.outputs.px4_version }}-arm64 || echo "⚠️ Warning: No ARM64 image found!"
|
||||
docker manifest inspect px4io/px4-dev:${{ needs.setup.outputs.px4_version }}-amd64 || echo "⚠️ Warning: No AMD64 image found!"
|
||||
docker manifest inspect ghcr.io/px4/px4-dev:${{ needs.setup.outputs.px4_version }}-arm64 || echo "⚠️ Warning: No ARM64 image found!"
|
||||
docker manifest inspect ghcr.io/px4/px4-dev:${{ needs.setup.outputs.px4_version }}-amd64 || echo "⚠️ Warning: No AMD64 image found!"
|
||||
docker manifest inspect px4io/px4-dev:${{ needs.setup.outputs.px4_version }}-arm64
|
||||
docker manifest inspect px4io/px4-dev:${{ needs.setup.outputs.px4_version }}-amd64
|
||||
docker manifest inspect ghcr.io/px4/px4-dev:${{ needs.setup.outputs.px4_version }}-arm64
|
||||
docker manifest inspect ghcr.io/px4/px4-dev:${{ needs.setup.outputs.px4_version }}-amd64
|
||||
|
||||
- name: Create and Push Multi-Arch Manifest for Docker Hub
|
||||
run: |
|
||||
|
||||
@@ -89,7 +89,17 @@ jobs:
|
||||
. /opt/ros/galactic/setup.bash
|
||||
mkdir -p /opt/px4_ws/src
|
||||
cd /opt/px4_ws/src
|
||||
git clone --recursive https://github.com/Auterion/px4-ros2-interface-lib.git
|
||||
# On a PR, target the branch we're merging into (main or release/X.Y).
|
||||
# On a direct push, fall back to the branch we're running on.
|
||||
BRANCH="${GITHUB_BASE_REF:-$GITHUB_REF_NAME}"
|
||||
REPO_URL="https://github.com/Auterion/px4-ros2-interface-lib.git"
|
||||
if git ls-remote --heads "$REPO_URL" "$BRANCH" | grep -q "$BRANCH"; then
|
||||
echo "Cloning px4-ros2-interface-lib with matching branch: $BRANCH"
|
||||
git clone --recursive --branch "$BRANCH" "$REPO_URL"
|
||||
else
|
||||
echo "Branch '$BRANCH' not found in px4-ros2-interface-lib, using default (main)"
|
||||
git clone --recursive "$REPO_URL"
|
||||
fi
|
||||
cd ..
|
||||
# Copy messages to ROS workspace
|
||||
"${PX4_DIR}/Tools/copy_to_ros_ws.sh" "$(pwd)"
|
||||
|
||||
@@ -65,7 +65,6 @@ adb shell "cd /usr/bin; /bin/ln -f -s px4 px4-logger"
|
||||
adb shell "cd /usr/bin; /bin/ln -f -s px4 px4-manual_control"
|
||||
adb shell "cd /usr/bin; /bin/ln -f -s px4 px4-mavlink"
|
||||
adb shell "cd /usr/bin; /bin/ln -f -s px4 px4-mavlink_bridge"
|
||||
adb shell "cd /usr/bin; /bin/ln -f -s px4 px4-mavlink_tests"
|
||||
adb shell "cd /usr/bin; /bin/ln -f -s px4 px4-mb12xx"
|
||||
adb shell "cd /usr/bin; /bin/ln -f -s px4 px4-mc_att_control"
|
||||
adb shell "cd /usr/bin; /bin/ln -f -s px4 px4-mc_pos_control"
|
||||
|
||||
@@ -72,20 +72,6 @@ endforeach()
|
||||
|
||||
|
||||
|
||||
# Mavlink test requires mavlink running
|
||||
add_test(NAME sitl-mavlink
|
||||
COMMAND $<TARGET_FILE:px4>
|
||||
-s ${PX4_SOURCE_DIR}/posix-configs/SITL/init/test/test_mavlink
|
||||
-t ${PX4_SOURCE_DIR}/test_data
|
||||
${PX4_SOURCE_DIR}/ROMFS/px4fmu_test
|
||||
WORKING_DIRECTORY ${SITL_WORKING_DIR}
|
||||
)
|
||||
|
||||
set_tests_properties(sitl-mavlink PROPERTIES FAIL_REGULAR_EXPRESSION "FAIL")
|
||||
set_tests_properties(sitl-mavlink PROPERTIES PASS_REGULAR_EXPRESSION "ALL TESTS PASSED")
|
||||
sanitizer_fail_test_on_error(sitl-mavlink)
|
||||
|
||||
|
||||
# IMU filtering
|
||||
add_test(NAME sitl-imu_filtering
|
||||
COMMAND $<TARGET_FILE:px4>
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/bin/sh
|
||||
# PX4 commands need the 'px4-' prefix in bash.
|
||||
# (px4-alias.sh is expected to be in the PATH)
|
||||
. px4-alias.sh
|
||||
|
||||
param load
|
||||
param set CBRK_SUPPLY_CHK 894281
|
||||
|
||||
dataman start
|
||||
|
||||
battery_simulator start
|
||||
simulator_mavlink start
|
||||
tone_alarm start
|
||||
pwm_out_sim start
|
||||
|
||||
ver all
|
||||
|
||||
mavlink start -x -u 14556 -r 2000000
|
||||
mavlink boot_complete
|
||||
|
||||
mavlink_tests
|
||||
|
||||
shutdown
|
||||
@@ -96,9 +96,16 @@ void TattuCan::Run()
|
||||
|
||||
while (receive(&received_frame) > 0) {
|
||||
|
||||
if (received_frame.payload_size == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
size_t payload_size = received_frame.payload_size - 1;
|
||||
// TODO: add check to prevent buffer overflow from a corrupt 'payload_size' value
|
||||
// TODO: AND look for TAIL_BYTE_START_OF_TRANSFER to indicate end of transfer. Untested...
|
||||
|
||||
if (offset + payload_size > sizeof(tattu_message)) {
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(((char *)&tattu_message) + offset, received_frame.payload, payload_size);
|
||||
offset += payload_size;
|
||||
}
|
||||
|
||||
@@ -197,6 +197,12 @@ int BST::probe()
|
||||
}
|
||||
|
||||
uint8_t *reply_raw = reinterpret_cast<uint8_t *>(&dev_info_reply);
|
||||
|
||||
if (dev_info_reply.length >= sizeof(dev_info_reply)) {
|
||||
PX4_ERR("invalid reply length: %u", dev_info_reply.length);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
uint8_t crc_calc = crc8(reinterpret_cast<uint8_t *>(&dev_info_reply.type), dev_info_reply.length - 1);
|
||||
uint8_t crc_recv = reply_raw[dev_info_reply.length];
|
||||
|
||||
@@ -205,6 +211,10 @@ int BST::probe()
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (dev_info_reply.payload.dev_name_len >= sizeof(dev_info_reply.payload.dev_name)) {
|
||||
dev_info_reply.payload.dev_name_len = sizeof(dev_info_reply.payload.dev_name) - 1;
|
||||
}
|
||||
|
||||
dev_info_reply.payload.dev_name[dev_info_reply.payload.dev_name_len] = '\0';
|
||||
PX4_DEBUG("device info: hardware ID: 0x%08X, firmware ID: 0x%04X, device name: %s",
|
||||
(int)swap_uint32(dev_info_reply.payload.hw_id), (int)swap_uint16(dev_info_reply.payload.fw_id),
|
||||
|
||||
@@ -59,13 +59,15 @@
|
||||
#ifndef PX4_AIRSPEEDDIRECTIONONTROLLER_HPP
|
||||
#define PX4_AIRSPEEDDIRECTIONONTROLLER_HPP
|
||||
|
||||
#include <matrix/math.hpp>
|
||||
#include <lib/mathlib/mathlib.h>
|
||||
|
||||
class AirspeedDirectionController
|
||||
{
|
||||
public:
|
||||
|
||||
AirspeedDirectionController();
|
||||
|
||||
|
||||
void setPGainFromPeriodAndDamping(float damping, float period) {p_gain_ = 4.f * M_PI_F * damping / math::max(period, FLT_EPSILON);}
|
||||
float controlHeading(const float heading_sp, const float heading, const float airspeed) const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -155,7 +155,7 @@ void Ekf::controlOpticalFlowFusion(const imuSample &imu_delayed)
|
||||
&& is_magnitude_good
|
||||
&& is_tilt_good
|
||||
&& (_flow_counter > 10)
|
||||
&& (isTerrainEstimateValid() || isHorizontalAidingActive())
|
||||
&& (isTerrainEstimateValid() || isHorizontalAidingActive() || (_height_sensor_ref == HeightSensor::RANGE))
|
||||
&& isTimedOut(_aid_src_optical_flow.time_last_fuse, (uint64_t)2e6); // Prevent rapid switching
|
||||
|
||||
// If the height is relative to the ground, terrain height cannot be observed.
|
||||
|
||||
@@ -115,6 +115,8 @@ FwLateralLongitudinalControl::parameters_update()
|
||||
|
||||
_tecs_alt_time_const_slew_rate.setSlewRate(TECS_ALT_TIME_CONST_SLEW_RATE);
|
||||
_tecs_alt_time_const_slew_rate.setForcedValue(_param_fw_t_h_error_tc.get() * _param_fw_thrtc_sc.get());
|
||||
|
||||
_airspeed_direction_control.setPGainFromPeriodAndDamping(_param_npfg_damping.get(), _param_npfg_period.get());
|
||||
}
|
||||
|
||||
void FwLateralLongitudinalControl::Run()
|
||||
|
||||
@@ -169,7 +169,9 @@ private:
|
||||
(ParamFloat<px4::params::FW_LND_THRTC_SC>) _param_fw_thrtc_sc,
|
||||
(ParamFloat<px4::params::FW_T_THR_LOW_HGT>) _param_fw_t_thr_low_hgt,
|
||||
(ParamFloat<px4::params::FW_WIND_ARSP_SC>) _param_fw_wind_arsp_sc,
|
||||
(ParamFloat<px4::params::FW_GND_SPD_MIN>) _param_fw_gnd_spd_min
|
||||
(ParamFloat<px4::params::FW_GND_SPD_MIN>) _param_fw_gnd_spd_min,
|
||||
(ParamFloat<px4::params::NPFG_DAMPING>) _param_npfg_damping,
|
||||
(ParamFloat<px4::params::NPFG_PERIOD>) _param_npfg_period
|
||||
)
|
||||
|
||||
hrt_abstime _last_time_loop_ran{};
|
||||
|
||||
@@ -143,10 +143,6 @@ px4_add_module(
|
||||
UNITY_BUILD
|
||||
)
|
||||
|
||||
if(PX4_TESTING)
|
||||
add_subdirectory(mavlink_tests)
|
||||
endif()
|
||||
|
||||
px4_add_unit_gtest(SRC MavlinkStatustextHandlerTest.cpp
|
||||
INCLUDES
|
||||
${MAVLINK_LIBRARY_DIR}
|
||||
|
||||
@@ -93,9 +93,7 @@ extern mavlink_status_t *mavlink_get_channel_status(uint8_t chan);
|
||||
extern mavlink_message_t *mavlink_get_channel_buffer(uint8_t chan);
|
||||
|
||||
#include <mavlink.h>
|
||||
#if !MAVLINK_FTP_UNIT_TEST
|
||||
#include <uAvionix.h>
|
||||
#endif
|
||||
|
||||
__END_DECLS
|
||||
|
||||
|
||||
@@ -43,8 +43,6 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "mavlink_ftp.h"
|
||||
#include "mavlink_tests/mavlink_ftp_test.h"
|
||||
|
||||
#include "mavlink_main.h"
|
||||
|
||||
using namespace time_literals;
|
||||
@@ -75,49 +73,22 @@ MavlinkFTP::get_size()
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MAVLINK_FTP_UNIT_TEST
|
||||
void
|
||||
MavlinkFTP::set_unittest_worker(ReceiveMessageFunc_t rcvMsgFunc, void *worker_data)
|
||||
{
|
||||
_utRcvMsgFunc = rcvMsgFunc;
|
||||
_worker_data = worker_data;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t
|
||||
MavlinkFTP::_getServerSystemId()
|
||||
{
|
||||
#ifdef MAVLINK_FTP_UNIT_TEST
|
||||
// We use fake ids when unit testing
|
||||
return MavlinkFtpTest::serverSystemId;
|
||||
#else
|
||||
// Not unit testing, use the real thing
|
||||
return _mavlink.get_system_id();
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t
|
||||
MavlinkFTP::_getServerComponentId()
|
||||
{
|
||||
#ifdef MAVLINK_FTP_UNIT_TEST
|
||||
// We use fake ids when unit testing
|
||||
return MavlinkFtpTest::serverComponentId;
|
||||
#else
|
||||
// Not unit testing, use the real thing
|
||||
return _mavlink.get_component_id();
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t
|
||||
MavlinkFTP::_getServerChannel()
|
||||
{
|
||||
#ifdef MAVLINK_FTP_UNIT_TEST
|
||||
// We use fake ids when unit testing
|
||||
return MavlinkFtpTest::serverChannel;
|
||||
#else
|
||||
// Not unit testing, use the real thing
|
||||
return _mavlink.get_channel();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@@ -173,11 +144,7 @@ MavlinkFTP::_process_request(
|
||||
if (payload->seq_number + 1 == last_payload->seq_number) {
|
||||
// this is the same request as the one we replied to last. It means the (n)ack got lost, and the GCS
|
||||
// resent the request
|
||||
#ifdef MAVLINK_FTP_UNIT_TEST
|
||||
_utRcvMsgFunc(last_reply, _worker_data);
|
||||
#else
|
||||
mavlink_msg_file_transfer_protocol_send_struct(_mavlink.get_channel(), last_reply);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -332,12 +299,7 @@ MavlinkFTP::_reply(mavlink_file_transfer_protocol_t *ftp_req)
|
||||
|
||||
PX4_DEBUG("FTP: %s seq_number: %" PRIu16, payload->opcode == kRspAck ? "Ack" : "Nak", payload->seq_number);
|
||||
|
||||
#ifdef MAVLINK_FTP_UNIT_TEST
|
||||
// Unit test hook is set, call that instead
|
||||
_utRcvMsgFunc(ftp_req, _worker_data);
|
||||
#else
|
||||
mavlink_msg_file_transfer_protocol_send_struct(_mavlink.get_channel(), ftp_req);
|
||||
#endif
|
||||
|
||||
}
|
||||
void MavlinkFTP::_constructPath(char *dst, int dst_len, const char *path) const
|
||||
@@ -362,6 +324,10 @@ MavlinkFTP::_workList(PayloadHeader *payload)
|
||||
{
|
||||
_constructPath(_work_buffer1, _work_buffer1_len, _data_as_cstring(payload));
|
||||
|
||||
if (!_validatePath(_work_buffer1)) {
|
||||
return kErrFailFileProtected;
|
||||
}
|
||||
|
||||
ErrorCode errorCode = kErrNone;
|
||||
unsigned offset = 0;
|
||||
|
||||
@@ -510,6 +476,10 @@ MavlinkFTP::_workOpen(PayloadHeader *payload, int oflag)
|
||||
|
||||
_constructPath(_work_buffer1, _work_buffer1_len, _data_as_cstring(payload));
|
||||
|
||||
if (!_validatePath(_work_buffer1)) {
|
||||
return kErrFailFileProtected;
|
||||
}
|
||||
|
||||
PX4_DEBUG("FTP: open '%s'", _work_buffer1);
|
||||
|
||||
uint32_t fileSize = 0;
|
||||
@@ -594,7 +564,7 @@ MavlinkFTP::_workRead(PayloadHeader *payload)
|
||||
MavlinkFTP::ErrorCode
|
||||
MavlinkFTP::_workBurst(PayloadHeader *payload, uint8_t target_system_id, uint8_t target_component_id)
|
||||
{
|
||||
if (payload->session != 0 && _session_info.fd < 0) {
|
||||
if (payload->session != 0 || _session_info.fd < 0) {
|
||||
PX4_DEBUG("_workBurst: no session or no fd");
|
||||
return kErrInvalidSession;
|
||||
}
|
||||
@@ -615,7 +585,7 @@ MavlinkFTP::_workBurst(PayloadHeader *payload, uint8_t target_system_id, uint8_t
|
||||
MavlinkFTP::ErrorCode
|
||||
MavlinkFTP::_workWrite(PayloadHeader *payload)
|
||||
{
|
||||
if (payload->session != 0 && _session_info.fd < 0) {
|
||||
if (payload->session != 0 || _session_info.fd < 0) {
|
||||
PX4_DEBUG("_workWrite: no session or no fd");
|
||||
return kErrInvalidSession;
|
||||
}
|
||||
@@ -652,7 +622,7 @@ MavlinkFTP::_workRemoveFile(PayloadHeader *payload)
|
||||
{
|
||||
_constructPath(_work_buffer1, _work_buffer1_len, _data_as_cstring(payload));
|
||||
|
||||
if (!_validatePathIsWritable(_work_buffer1)) {
|
||||
if (!_validatePath(_work_buffer1) || !_validatePathIsWritable(_work_buffer1)) {
|
||||
return kErrFailFileProtected;
|
||||
}
|
||||
|
||||
@@ -676,7 +646,7 @@ MavlinkFTP::_workTruncateFile(PayloadHeader *payload)
|
||||
_constructPath(_work_buffer1, _work_buffer1_len, _data_as_cstring(payload));
|
||||
payload->size = 0;
|
||||
|
||||
if (!_validatePathIsWritable(_work_buffer1)) {
|
||||
if (!_validatePath(_work_buffer1) || !_validatePathIsWritable(_work_buffer1)) {
|
||||
return kErrFailFileProtected;
|
||||
}
|
||||
|
||||
@@ -837,7 +807,7 @@ MavlinkFTP::_workRename(PayloadHeader *payload)
|
||||
_constructPath(_work_buffer1, _work_buffer1_len, ptr);
|
||||
_constructPath(_work_buffer2, _work_buffer2_len, ptr + oldpath_sz + 1);
|
||||
|
||||
if (!_validatePathIsWritable(_work_buffer2)) {
|
||||
if (!_validatePath(_work_buffer1) || !_validatePath(_work_buffer2) || !_validatePathIsWritable(_work_buffer2)) {
|
||||
return kErrFailFileProtected;
|
||||
}
|
||||
|
||||
@@ -860,7 +830,7 @@ MavlinkFTP::_workRemoveDirectory(PayloadHeader *payload)
|
||||
{
|
||||
_constructPath(_work_buffer1, _work_buffer1_len, _data_as_cstring(payload));
|
||||
|
||||
if (!_validatePathIsWritable(_work_buffer1)) {
|
||||
if (!_validatePath(_work_buffer1) || !_validatePathIsWritable(_work_buffer1)) {
|
||||
return kErrFailFileProtected;
|
||||
}
|
||||
|
||||
@@ -883,7 +853,7 @@ MavlinkFTP::_workCreateDirectory(PayloadHeader *payload)
|
||||
{
|
||||
_constructPath(_work_buffer1, _work_buffer1_len, _data_as_cstring(payload));
|
||||
|
||||
if (!_validatePathIsWritable(_work_buffer1)) {
|
||||
if (!_validatePath(_work_buffer1) || !_validatePathIsWritable(_work_buffer1)) {
|
||||
return kErrFailFileProtected;
|
||||
}
|
||||
|
||||
@@ -908,6 +878,10 @@ MavlinkFTP::_workCalcFileCRC32(PayloadHeader *payload)
|
||||
ssize_t bytes_read;
|
||||
_constructPath(_work_buffer2, _work_buffer2_len, _data_as_cstring(payload));
|
||||
|
||||
if (!_validatePath(_work_buffer2)) {
|
||||
return kErrFailFileProtected;
|
||||
}
|
||||
|
||||
int fd = ::open(_work_buffer2, O_RDONLY);
|
||||
|
||||
if (fd < 0) {
|
||||
@@ -1043,7 +1017,6 @@ void MavlinkFTP::send()
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef MAVLINK_FTP_UNIT_TEST
|
||||
// Skip send if not enough room
|
||||
unsigned max_bytes_to_send = _mavlink.get_free_tx_buf();
|
||||
PX4_DEBUG("MavlinkFTP::send max_bytes_to_send(%u) get_free_tx_buf(%u)", max_bytes_to_send, _mavlink.get_free_tx_buf());
|
||||
@@ -1052,8 +1025,6 @@ void MavlinkFTP::send()
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Send stream packets until buffer is full
|
||||
|
||||
bool more_data;
|
||||
@@ -1117,8 +1088,6 @@ void MavlinkFTP::send()
|
||||
_session_info.stream_download = false;
|
||||
|
||||
} else {
|
||||
#ifndef MAVLINK_FTP_UNIT_TEST
|
||||
|
||||
if (max_bytes_to_send < (get_size() * 2)) {
|
||||
more_data = false;
|
||||
|
||||
@@ -1130,14 +1099,10 @@ void MavlinkFTP::send()
|
||||
}
|
||||
|
||||
} else {
|
||||
#endif
|
||||
more_data = true;
|
||||
payload->burst_complete = false;
|
||||
#ifndef MAVLINK_FTP_UNIT_TEST
|
||||
max_bytes_to_send -= get_size();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
ftp_msg.target_system = _session_info.stream_target_system_id;
|
||||
@@ -1147,6 +1112,36 @@ void MavlinkFTP::send()
|
||||
} while (more_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject paths containing ".." components to prevent directory traversal
|
||||
* outside of _root_dir. Walks the path checking each component between
|
||||
* '/' separators. A component of exactly ".." (followed by '/' or end
|
||||
* of string) is rejected.
|
||||
*
|
||||
* Examples:
|
||||
* "/fs/microsd/logs" -> allowed
|
||||
* "/fs/microsd/../etc" -> rejected
|
||||
* "../../tmp/pwned" -> rejected
|
||||
* "/fs/microsd/logs/.." -> rejected
|
||||
* "/fs/microsd/..hidden" -> allowed (not a ".." component)
|
||||
*/
|
||||
bool MavlinkFTP::_validatePath(const char *path)
|
||||
{
|
||||
const char *p = path;
|
||||
|
||||
while (*p != '\0') {
|
||||
if ((p == path || *(p - 1) == '/') && p[0] == '.' && p[1] == '.'
|
||||
&& (p[2] == '/' || p[2] == '\0')) {
|
||||
PX4_ERR("FTP: rejecting path traversal in %s", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
p++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MavlinkFTP::_validatePathIsWritable(const char *path)
|
||||
{
|
||||
#ifdef __PX4_NUTTX
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
|
||||
#include "mavlink_bridge_header.h"
|
||||
|
||||
class MavlinkFtpTest;
|
||||
class Mavlink;
|
||||
|
||||
/// MAVLink remote file server. Support FTP like commands using MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL message.
|
||||
@@ -64,13 +63,6 @@ public:
|
||||
/// Handle possible FTP message
|
||||
void handle_message(const mavlink_message_t *msg);
|
||||
|
||||
typedef void (*ReceiveMessageFunc_t)(const mavlink_file_transfer_protocol_t *ftp_req, void *worker_data);
|
||||
|
||||
/// @brief Sets up the server to run in unit test mode.
|
||||
/// @param rcvmsgFunc Function which will be called to handle outgoing mavlink messages.
|
||||
/// @param worker_data Data to pass to worker
|
||||
void set_unittest_worker(ReceiveMessageFunc_t rcvMsgFunc, void *worker_data);
|
||||
|
||||
/// @brief This is the payload which is in mavlink_file_transfer_protocol_t.payload.
|
||||
/// This needs to be packed, because it's typecasted from mavlink_file_transfer_protocol_t.payload, which starts
|
||||
/// at a 3 byte offset, causing an unaligned access to seq_number and offset
|
||||
@@ -156,6 +148,7 @@ private:
|
||||
*/
|
||||
void _constructPath(char *dst, int dst_len, const char *path) const;
|
||||
|
||||
bool _validatePath(const char *path);
|
||||
bool _validatePathIsWritable(const char *path);
|
||||
|
||||
/**
|
||||
@@ -183,9 +176,6 @@ private:
|
||||
};
|
||||
struct SessionInfo _session_info {}; ///< Session info, fd=-1 for no active session
|
||||
|
||||
ReceiveMessageFunc_t _utRcvMsgFunc{}; ///< Unit test override for mavlink message sending
|
||||
void *_worker_data{nullptr}; ///< Additional parameter to _utRcvMsgFunc;
|
||||
|
||||
Mavlink &_mavlink;
|
||||
|
||||
/* do not allow copying this class */
|
||||
@@ -200,20 +190,13 @@ private:
|
||||
hrt_abstime _last_work_buffer_access{0}; ///< timestamp when the buffers were last accessed
|
||||
|
||||
// prepend a root directory to each file/dir access to avoid enumerating the full FS tree (e.g. on Linux).
|
||||
// Note that requests can still fall outside of the root dir by using ../..
|
||||
#ifdef MAVLINK_FTP_UNIT_TEST
|
||||
static constexpr const char _root_dir[] = "";
|
||||
#else
|
||||
// Path traversal via ".." is rejected by _validatePath().
|
||||
static constexpr const char _root_dir[] = PX4_ROOTFSDIR;
|
||||
#endif
|
||||
static constexpr const int _root_dir_len = sizeof(_root_dir) - 1;
|
||||
|
||||
bool _last_reply_valid = false;
|
||||
uint8_t _last_reply[MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL_LEN - MAVLINK_MSG_FILE_TRANSFER_PROTOCOL_FIELD_PAYLOAD_LEN
|
||||
+ sizeof(PayloadHeader) + sizeof(uint32_t)];
|
||||
|
||||
// Mavlink test needs to be able to call send
|
||||
friend class MavlinkFtpTest;
|
||||
|
||||
int _our_errno {0};
|
||||
};
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
############################################################################
|
||||
#
|
||||
# Copyright (c) 2015 PX4 Development Team. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# 3. Neither the name PX4 nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
px4_add_module(
|
||||
MODULE modules__mavlink__mavlink_tests
|
||||
MAIN mavlink_tests
|
||||
STACK_MAIN 8192
|
||||
INCLUDES
|
||||
${MAVLINK_LIBRARY_DIR}
|
||||
${MAVLINK_LIBRARY_DIR}/${CONFIG_MAVLINK_DIALECT}
|
||||
COMPILE_FLAGS
|
||||
-DMAVLINK_FTP_UNIT_TEST
|
||||
#-DMAVLINK_FTP_DEBUG
|
||||
-DMavlinkStream=MavlinkStreamTest
|
||||
-DMavlinkFTP=MavlinkFTPTest
|
||||
-Wno-cast-align # TODO: fix and enable
|
||||
-Wno-address-of-packed-member # TODO: fix in c_library_v2
|
||||
-Wno-double-promotion # The fix has been proposed as PR upstream (2020-03-08)
|
||||
SRCS
|
||||
mavlink_tests.cpp
|
||||
mavlink_ftp_test.cpp
|
||||
../mavlink_stream.cpp
|
||||
../mavlink_ftp.cpp
|
||||
DEPENDS
|
||||
mavlink_c_generate
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,139 +0,0 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/// @file mavlink_ftp_test.h
|
||||
/// @author Don Gagne <don@thegagnes.com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unit_test.h>
|
||||
#include "../mavlink_bridge_header.h"
|
||||
#include "../mavlink_ftp.h"
|
||||
#include "../mavlink_main.h"
|
||||
|
||||
class MavlinkFtpTest : public UnitTest
|
||||
{
|
||||
public:
|
||||
MavlinkFtpTest();
|
||||
virtual ~MavlinkFtpTest() = default;
|
||||
|
||||
virtual bool run_tests(void);
|
||||
|
||||
static void receive_message_handler_generic(const mavlink_file_transfer_protocol_t *ftp_req, void *worker_data);
|
||||
|
||||
/// Worker data for stream handler
|
||||
struct BurstInfo {
|
||||
MavlinkFtpTest *ftp_test_class;
|
||||
int burst_state;
|
||||
bool single_packet_file;
|
||||
uint32_t file_size;
|
||||
uint8_t *file_bytes;
|
||||
};
|
||||
|
||||
static void receive_message_handler_burst(const mavlink_file_transfer_protocol_t *ftp_req, void *worker_data);
|
||||
|
||||
static const uint8_t serverSystemId = 50; ///< System ID for server
|
||||
static const uint8_t serverComponentId = 1; ///< Component ID for server
|
||||
static const uint8_t serverChannel = 0; ///< Channel to send to
|
||||
|
||||
static const uint8_t clientSystemId = 1; ///< System ID for client
|
||||
static const uint8_t clientComponentId = 0; ///< Component ID for client
|
||||
|
||||
// We don't want any of these
|
||||
MavlinkFtpTest(const MavlinkFtpTest &);
|
||||
MavlinkFtpTest &operator=(const MavlinkFtpTest &);
|
||||
|
||||
private:
|
||||
virtual void _init(void);
|
||||
virtual void _cleanup(void);
|
||||
|
||||
bool _create_test_files(void);
|
||||
bool _remove_test_files(void);
|
||||
bool _ack_test(void);
|
||||
bool _bad_opcode_test(void);
|
||||
bool _bad_datasize_test(void);
|
||||
bool _list_test(void);
|
||||
bool _list_eof_test(void);
|
||||
bool _open_badfile_test(void);
|
||||
bool _open_terminate_test(void);
|
||||
bool _terminate_badsession_test(void);
|
||||
bool _read_test(void);
|
||||
bool _read_badsession_test(void);
|
||||
bool _burst_test(void);
|
||||
bool _removedirectory_test(void);
|
||||
bool _createdirectory_test(void);
|
||||
bool _removefile_test(void);
|
||||
|
||||
void _receive_message_handler_generic(const mavlink_file_transfer_protocol_t *ftp_req);
|
||||
bool _setup_ftp_msg(const MavlinkFTP::PayloadHeader *payload_header,
|
||||
const uint8_t *data, const uint8_t data_len,
|
||||
mavlink_message_t *msg);
|
||||
bool _decode_message(const mavlink_file_transfer_protocol_t *ftp_msg, const MavlinkFTP::PayloadHeader **payload);
|
||||
bool _send_receive_msg(MavlinkFTP::PayloadHeader *payload_header,
|
||||
const uint8_t *data,
|
||||
const size_t data_len,
|
||||
const MavlinkFTP::PayloadHeader **payload_reply);
|
||||
void _cleanup_microsd(void);
|
||||
|
||||
/// A single download test case
|
||||
struct DownloadTestCase {
|
||||
const char *file;
|
||||
const uint16_t length;
|
||||
bool singlePacketRead;
|
||||
bool exactlyFillPacket;
|
||||
};
|
||||
|
||||
/// The set of test cases for download testing
|
||||
static const DownloadTestCase _rgDownloadTestCases[];
|
||||
|
||||
/// States for stream download handler
|
||||
enum {
|
||||
burst_state_first_ack,
|
||||
burst_state_last_ack,
|
||||
burst_state_nak_eof,
|
||||
burst_state_complete
|
||||
};
|
||||
|
||||
bool _receive_message_handler_burst(const mavlink_file_transfer_protocol_t *ftp_req, BurstInfo *burst_info);
|
||||
|
||||
MavlinkFTP *_ftp_server;
|
||||
Mavlink _mavlink;
|
||||
uint16_t _expected_seq_number;
|
||||
|
||||
mavlink_file_transfer_protocol_t _reply_msg;
|
||||
|
||||
static const char _unittest_microsd_dir[];
|
||||
static const char _unittest_microsd_file[];
|
||||
};
|
||||
|
||||
bool mavlink_ftp_test(void);
|
||||
@@ -1,47 +0,0 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file mavlink_tests.cpp
|
||||
*/
|
||||
|
||||
#include <systemlib/err.h>
|
||||
|
||||
#include "mavlink_ftp_test.h"
|
||||
|
||||
extern "C" __EXPORT int mavlink_tests_main(int argc, char *argv[]);
|
||||
|
||||
int mavlink_tests_main(int argc, char *argv[])
|
||||
{
|
||||
return mavlink_ftp_test() ? 0 : -1;
|
||||
}
|
||||
@@ -79,6 +79,14 @@ public:
|
||||
const z_loaned_bytes_t *payload = z_sample_payload(sample);
|
||||
size_t len = z_bytes_len(payload);
|
||||
|
||||
// Validate payload size to prevent stack overflow from untrusted input.
|
||||
// CDR payload = 4-byte header + serialized data, which should not exceed o_size + 4.
|
||||
const size_t max_payload_size = _uorb_meta->o_size + 4;
|
||||
|
||||
if (len > max_payload_size || len < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(Z_FEATURE_UNSTABLE_API)
|
||||
// Check if payload is contiguous so we can decode directly on that pointer
|
||||
z_view_slice_t view;
|
||||
|
||||
Reference in New Issue
Block a user