Files
ardupilot/libraries/AP_CANManager/AP_CANManager.cpp
T
Thomas Watson edca856684 AP_CANManager: fix heap corruption when CAN log buffer fills
Once the CAN log buffer gets near to full, the check for remaining space
can pass even though there is insufficient space left in the buffer
for the actual log message after formatting.

The space for the tag is correctly measured, so the `snprintf` will
never perform a short write. However, the `vsnprintf` will. Though it
does not write too much, it pushes `_log_pos` past the end of the buffer
as it returns the amount that would be written, rather than the amount
that actually was written.

In this case, the test on the next call of the function aims to reset
`_log_pos` to the start. Unfortunately, as that variable is unsigned,
the subtraction from `LOG_BUFFER_SIZE` will result in a large positive
number, falsely stating that there is enough space. This will also then
give the `snprintf` and `vsnprintf` a large positive space for them to
write into, so they will write past the end of the buffer and corrupt
the heap and crash the system.

Fix by making the variable and comparison properly signed. This will
give a problem if log messages get into the billions of characters, but
this is unlikely to occur.
2026-05-18 20:58:47 -05:00

478 lines
15 KiB
C++

/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* AP_CANManager - board specific configuration for CAN interface
*/
#include <AP_HAL/AP_HAL.h>
#include <AP_Common/AP_Common.h>
#include "AP_CANManager.h"
#if HAL_CANMANAGER_ENABLED
#include <AP_BoardConfig/AP_BoardConfig.h>
#include <AP_Vehicle/AP_Vehicle_Type.h>
#include <AP_DroneCAN/AP_DroneCAN.h>
#include <AP_KDECAN/AP_KDECAN.h>
#include <AP_SerialManager/AP_SerialManager.h>
#include <AP_PiccoloCAN/AP_PiccoloCAN.h>
#include <AP_EFI/AP_EFI_NWPMU.h>
#include <GCS_MAVLink/GCS.h>
#if CONFIG_HAL_BOARD == HAL_BOARD_LINUX
#include <AP_HAL_Linux/CANSocketIface.h>
#elif CONFIG_HAL_BOARD == HAL_BOARD_SITL
#include <AP_HAL_SITL/CANSocketIface.h>
#elif CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
#include <hal.h>
#include <AP_HAL_ChibiOS/CANIface.h>
#endif
#include <AP_Common/ExpandingString.h>
#include <AP_Common/sorting.h>
#include <AP_Logger/AP_Logger.h>
#define LOG_TAG "CANMGR"
#define LOG_BUFFER_SIZE 1024
extern const AP_HAL::HAL& hal;
// table of user settable parameters
const AP_Param::GroupInfo AP_CANManager::var_info[] = {
#if HAL_NUM_CAN_IFACES > 0
// @Group: P1_
// @Path: ../AP_CANManager/AP_CANIfaceParams.cpp
AP_SUBGROUPINFO(_interfaces[0], "P1_", 1, AP_CANManager, AP_CANManager::CANIface_Params),
#endif
#if HAL_NUM_CAN_IFACES > 1
// @Group: P2_
// @Path: ../AP_CANManager/AP_CANIfaceParams.cpp
AP_SUBGROUPINFO(_interfaces[1], "P2_", 2, AP_CANManager, AP_CANManager::CANIface_Params),
#endif
#if HAL_NUM_CAN_IFACES > 2
// @Group: P3_
// @Path: ../AP_CANManager/AP_CANIfaceParams.cpp
AP_SUBGROUPINFO(_interfaces[2], "P3_", 3, AP_CANManager, AP_CANManager::CANIface_Params),
#endif
#if HAL_MAX_CAN_PROTOCOL_DRIVERS > 0
// @Group: D1_
// @Path: ../AP_CANManager/AP_CANManager_CANDriver_Params.cpp
AP_SUBGROUPINFO(_drv_param[0], "D1_", 4, AP_CANManager, AP_CANManager::CANDriver_Params),
#endif
#if HAL_MAX_CAN_PROTOCOL_DRIVERS > 1
// @Group: D2_
// @Path: ../AP_CANManager/AP_CANManager_CANDriver_Params.cpp
AP_SUBGROUPINFO(_drv_param[1], "D2_", 5, AP_CANManager, AP_CANManager::CANDriver_Params),
#endif
#if HAL_MAX_CAN_PROTOCOL_DRIVERS > 2
// @Group: D3_
// @Path: ../AP_CANManager/AP_CANManager_CANDriver_Params.cpp
AP_SUBGROUPINFO(_drv_param[2], "D3_", 6, AP_CANManager, AP_CANManager::CANDriver_Params),
#endif
#if AP_CAN_SLCAN_ENABLED
// @Group: SLCAN_
// @Path: ../AP_CANManager/AP_SLCANIface.cpp
AP_SUBGROUPINFO(_slcan_interface, "SLCAN_", 7, AP_CANManager, SLCAN::CANIface),
#endif
// @Param: LOGLEVEL
// @DisplayName: Loglevel
// @Description: Loglevel for recording initialisation and debug information from CAN Interface
// @Range: 0 4
// @Values: 0: Log None, 1: Log Error, 2: Log Warning and below, 3: Log Info and below, 4: Log Everything
// @User: Advanced
AP_GROUPINFO("LOGLEVEL", 8, AP_CANManager, _loglevel, AP_CANManager::LOG_NONE),
AP_GROUPEND
};
AP_CANManager *AP_CANManager::_singleton;
AP_CANManager::AP_CANManager()
{
AP_Param::setup_object_defaults(this, var_info);
if (_singleton != nullptr) {
AP_HAL::panic("AP_CANManager must be singleton");
}
_singleton = this;
}
#if !AP_TEST_DRONECAN_DRIVERS
void AP_CANManager::init()
{
WITH_SEMAPHORE(_sem);
// we need to mutate the HAL to install new CAN interfaces
AP_HAL::HAL& hal_mutable = AP_HAL::get_HAL_mutable();
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
if (AP::sitl() == nullptr) {
AP_HAL::panic("CANManager: SITL not initialised!");
}
#endif
// We only allocate log buffer only when under debug
if (_loglevel != AP_CANManager::LOG_NONE) {
_log_buf = NEW_NOTHROW char[LOG_BUFFER_SIZE];
_log_pos = 0;
}
#if AP_CAN_SLCAN_ENABLED
//Reset all SLCAN related params that needs resetting at boot
_slcan_interface.reset_params();
#endif
AP_CAN::Protocol drv_type[HAL_MAX_CAN_PROTOCOL_DRIVERS] = {};
// loop through interfaces and allocate and initialise Iface,
// Also allocate Driver objects, and add interfaces to them
for (uint8_t i = 0; i < HAL_NUM_CAN_IFACES; i++) {
// Get associated Driver to the interface
uint8_t drv_num = _interfaces[i]._driver_number;
if (drv_num == 0 || drv_num > HAL_MAX_CAN_PROTOCOL_DRIVERS) {
continue;
}
drv_num--;
if (hal_mutable.can[i] == nullptr) {
// So if this interface is not allocated allocate it here,
// also pass the index of the CANBus
hal_mutable.can[i] = NEW_NOTHROW HAL_CANIface(i);
}
// Initialise the interface we just allocated
if (hal_mutable.can[i] == nullptr) {
continue;
}
AP_HAL::CANIface* iface = hal_mutable.can[i];
// Find the driver type that we need to allocate and register this interface with
drv_type[drv_num] = (AP_CAN::Protocol) _drv_param[drv_num]._driver_type.get();
bool can_initialised = false;
// Check if this interface need hooking up to slcan passthrough
// instead of a driver
#if AP_CAN_SLCAN_ENABLED
if (_slcan_interface.init_passthrough(i)) {
// we have slcan bridge setup pass that on as can iface
can_initialised = hal_mutable.can[i]->init(_interfaces[i]._bitrate, _interfaces[i]._fdbitrate*1000000);
iface = &_slcan_interface;
} else {
#else
if (true) {
#endif
can_initialised = hal_mutable.can[i]->init(_interfaces[i]._bitrate, _interfaces[i]._fdbitrate*1000000);
}
if (!can_initialised) {
log_text(AP_CANManager::LOG_ERROR, LOG_TAG, "Failed to initialise CAN Interface %d", i+1);
continue;
}
log_text(AP_CANManager::LOG_INFO, LOG_TAG, "CAN Interface %d initialized well", i + 1);
if (_drivers[drv_num] != nullptr) {
//We already initialised the driver just add interface and move on
log_text(AP_CANManager::LOG_INFO, LOG_TAG, "Adding Interface %d to Driver %d", i + 1, drv_num + 1);
_drivers[drv_num]->add_interface(iface);
continue;
}
if (_num_drivers >= HAL_MAX_CAN_PROTOCOL_DRIVERS) {
// We are exceeding number of drivers,
// this can't be happening time to panic
AP_BoardConfig::config_error("Max number of CAN Drivers exceeded\n\r");
}
// Allocate the set type of Driver
switch (drv_type[drv_num]) {
#if HAL_ENABLE_DRONECAN_DRIVERS
case AP_CAN::Protocol::DroneCAN:
_drivers[drv_num] = _drv_param[drv_num]._uavcan = NEW_NOTHROW AP_DroneCAN(drv_num);
if (_drivers[drv_num] == nullptr) {
AP_BoardConfig::allocation_error("uavcan %d", i + 1);
continue;
}
AP_Param::load_object_from_eeprom((AP_DroneCAN*)_drivers[drv_num], AP_DroneCAN::var_info);
break;
#endif
#if AP_PICCOLOCAN_ENABLED
case AP_CAN::Protocol::PiccoloCAN:
_drivers[drv_num] = _drv_param[drv_num]._piccolocan = NEW_NOTHROW AP_PiccoloCAN;
if (_drivers[drv_num] == nullptr) {
AP_BoardConfig::allocation_error("PiccoloCAN %d", drv_num + 1);
continue;
}
AP_Param::load_object_from_eeprom((AP_PiccoloCAN*)_drivers[drv_num], AP_PiccoloCAN::var_info);
break;
#endif
default:
continue;
}
_num_drivers++;
// Hook this interface to the selected Driver Type
_drivers[drv_num]->add_interface(iface);
log_text(AP_CANManager::LOG_INFO, LOG_TAG, "Adding Interface %d to Driver %d", i + 1, drv_num + 1);
}
for (uint8_t drv_num = 0; drv_num < HAL_MAX_CAN_PROTOCOL_DRIVERS; drv_num++) {
//initialise all the Drivers
// Cache the driver type, initialized or not, so we can detect that it is in the params at boot via get_driver_type().
// This allows drivers that are initialized by CANSensor instead of CANManager to know if they should init or not
_driver_type_cache[drv_num] = drv_type[drv_num];
if (_drivers[drv_num] == nullptr) {
continue;
}
_drivers[drv_num]->init(drv_num);
}
#if AP_CAN_LOGGING_ENABLED
hal.scheduler->register_io_process(FUNCTOR_BIND_MEMBER(&AP_CANManager::check_logging_enable, void));
#endif
}
#else
void AP_CANManager::init()
{
WITH_SEMAPHORE(_sem);
for (uint8_t i = 0; i < HAL_NUM_CAN_IFACES; i++) {
if ((AP_CAN::Protocol) _drv_param[i]._driver_type.get() == AP_CAN::Protocol::DroneCAN) {
_drivers[i] = _drv_param[i]._uavcan = NEW_NOTHROW AP_DroneCAN(i);
if (_drivers[i] == nullptr) {
AP_BoardConfig::allocation_error("uavcan %d", i + 1);
continue;
}
AP_Param::load_object_from_eeprom((AP_DroneCAN*)_drivers[i], AP_DroneCAN::var_info);
_drivers[i]->init(i);
_driver_type_cache[i] = (AP_CAN::Protocol) _drv_param[i]._driver_type.get();
}
}
}
#endif
/*
register a new CAN driver
*/
bool AP_CANManager::register_driver(AP_CAN::Protocol dtype, AP_CANDriver *driver)
{
WITH_SEMAPHORE(_sem);
// we need to mutate the HAL to install new CAN interfaces
AP_HAL::HAL& hal_mutable = AP_HAL::get_HAL_mutable();
for (uint8_t i = 0; i < HAL_NUM_CAN_IFACES; i++) {
uint8_t drv_num = _interfaces[i]._driver_number;
if (drv_num == 0 || drv_num > HAL_MAX_CAN_PROTOCOL_DRIVERS) {
continue;
}
// from 1 based to 0 based
drv_num--;
if (dtype != (AP_CAN::Protocol)_drv_param[drv_num]._driver_type.get()) {
continue;
}
if (_drivers[drv_num] != nullptr) {
continue;
}
if (_num_drivers >= HAL_MAX_CAN_PROTOCOL_DRIVERS) {
continue;
}
if (hal_mutable.can[i] == nullptr) {
// if this interface is not allocated allocate it here,
// also pass the index of the CANBus
hal_mutable.can[i] = NEW_NOTHROW HAL_CANIface(i);
}
// Initialise the interface we just allocated
if (hal_mutable.can[i] == nullptr) {
continue;
}
AP_HAL::CANIface* iface = hal_mutable.can[i];
_drivers[drv_num] = driver;
_drivers[drv_num]->add_interface(iface);
log_text(AP_CANManager::LOG_INFO, LOG_TAG, "Adding Interface %d to Driver %d", i + 1, drv_num + 1);
_drivers[drv_num]->init(drv_num);
_driver_type_cache[drv_num] = dtype;
_num_drivers++;
return true;
}
return false;
}
// register a new auxillary sensor driver for 11 bit address frames
bool AP_CANManager::register_11bit_driver(AP_CAN::Protocol dtype, CANSensor *sensor, uint8_t &driver_index)
{
WITH_SEMAPHORE(_sem);
for (uint8_t i = 0; i < HAL_NUM_CAN_IFACES; i++) {
uint8_t drv_num = _interfaces[i]._driver_number;
if (drv_num == 0 || drv_num > HAL_MAX_CAN_PROTOCOL_DRIVERS) {
continue;
}
// from 1 based to 0 based
drv_num--;
if (dtype != (AP_CAN::Protocol)_drv_param[drv_num]._driver_type_11bit.get()) {
continue;
}
if (_drivers[drv_num] != nullptr &&
_drivers[drv_num]->add_11bit_driver(sensor)) {
driver_index = drv_num;
return true;
}
}
return false;
}
// Method used by CAN related library methods to report status and debug info
// The result of this method can be accessed via ftp get @SYS/can_log.txt
void AP_CANManager::log_text(AP_CANManager::LogLevel loglevel, const char *tag, const char *fmt, ...)
{
if (_log_buf == nullptr) {
return;
}
if (loglevel > _loglevel) {
return;
}
WITH_SEMAPHORE(_sem);
if ((LOG_BUFFER_SIZE - _log_pos) < (int32_t)(10 + strlen(tag) + strlen(fmt))) {
// reset log pos
_log_pos = 0;
}
//Tag Log Message
const char *log_level_tag = "";
switch (loglevel) {
case AP_CANManager::LOG_DEBUG :
log_level_tag = "DEBUG";
break;
case AP_CANManager::LOG_INFO :
log_level_tag = "INFO";
break;
case AP_CANManager::LOG_WARNING :
log_level_tag = "WARN";
break;
case AP_CANManager::LOG_ERROR :
log_level_tag = "ERROR";
break;
case AP_CANManager::LOG_NONE:
return;
}
_log_pos += hal.util->snprintf(&_log_buf[_log_pos], LOG_BUFFER_SIZE - _log_pos, "\n%s %s :", log_level_tag, tag);
va_list arg_list;
va_start(arg_list, fmt);
_log_pos += hal.util->vsnprintf(&_log_buf[_log_pos], LOG_BUFFER_SIZE - _log_pos, fmt, arg_list);
va_end(arg_list);
}
// log retrieve method used by file sys method to report can log
void AP_CANManager::log_retrieve(ExpandingString &str) const
{
if (_log_buf == nullptr) {
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "Log buffer not available");
return;
}
str.append(_log_buf, _log_pos);
}
#if AP_CAN_LOGGING_ENABLED
/*
handler for CAN frames for frame logging
*/
void AP_CANManager::can_logging_callback(uint8_t bus, const AP_HAL::CANFrame &frame, AP_HAL::CANIface::CanIOFlags flags)
{
#if HAL_CANFD_SUPPORTED
if (frame.canfd) {
struct log_CAFD pkt {
LOG_PACKET_HEADER_INIT(LOG_CAFD_MSG),
time_us : AP_HAL::micros64(),
bus : bus,
id : frame.id,
dlc : frame.dlc
};
memcpy(pkt.data, frame.data, frame.dlcToDataLength(frame.dlc));
AP::logger().WriteBlock(&pkt, sizeof(pkt));
return;
}
#endif
struct log_CANF pkt {
LOG_PACKET_HEADER_INIT(LOG_CANF_MSG),
time_us : AP_HAL::micros64(),
bus : bus,
id : frame.id,
dlc : frame.dlc
};
memcpy(pkt.data, frame.data, frame.dlc);
AP::logger().WriteBlock(&pkt, sizeof(pkt));
}
/*
see if we need to enable/disable the CAN logging callback
*/
void AP_CANManager::check_logging_enable(void)
{
for (uint8_t i = 0; i < HAL_NUM_CAN_IFACES; i++) {
const bool enabled = _interfaces[i].option_is_set(CANIface_Params::Options::LOG_ALL_FRAMES);
uint8_t &logging_id = _interfaces[i].logging_id;
auto *can = hal.can[i];
if (can == nullptr) {
continue;
}
if (enabled && logging_id == 0) {
can->register_frame_callback(
FUNCTOR_BIND_MEMBER(&AP_CANManager::can_logging_callback, void, uint8_t, const AP_HAL::CANFrame &, AP_HAL::CANIface::CanIOFlags),
logging_id);
} else if (!enabled && logging_id != 0) {
can->unregister_frame_callback(logging_id);
}
}
}
#endif // AP_CAN_LOGGING_ENABLED
AP_CANManager& AP::can()
{
return *AP_CANManager::get_singleton();
}
#endif