Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions FORK_PRS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ become its own scoped PR. Issue numbers filled in once created.
| # | Issue | Source fork | Commit(s) | Summary | Tier |
|---|-------|-------------|-----------|---------|------|
| 1 | ✅ [#14](https://github.com/fugo101/microlink/issues/14) (done) | [`cplewes/microlink`](https://github.com/cplewes/microlink) | `38602ab0`, `b25b1eee` | DERP TLS context leak on every failed `ml_derp_connect()` (~3-8KB/attempt, verified on hardware). Pre-existing issue filed 2026-08-17 from `UPSTREAM_PRS.md`'s "mineable from #22" note; enriched here with the cplewes source instead of building `derp_tls_abort()` from scratch. Adapted (not literal cherry-pick, our fork lacks entropy/ctr_drbg fields) into `ml_derp.c`'s `derp_free_tls_state()` + `fail_tls` goto path. | 1 |
| 2 | [#21](https://github.com/fugo101/microlink/issues/21) | [`cplewes/microlink`](https://github.com/cplewes/microlink) | `a415d646` | Teardown UAF: `microlink_stop()`/`destroy()` didn't join worker tasks before freeing context; replaces "sleep 3s and hope" with a real per-task liveness bitmask + reap-orphans path | 1 |
| 3 | [#22](https://github.com/fugo101/microlink/issues/22) | [`cplewes/microlink`](https://github.com/cplewes/microlink) | `7120dfa4` | Three crash sites triggered by captive-portal DNS failures: DERP double-init dangling pointer, `ml_coord_task` touching a freed event group post-destroy, corrupt-state races during parallel teardown | 1 |
| 2 | [#21](https://github.com/fugo101/microlink/issues/21) (done) | [`cplewes/microlink`](https://github.com/cplewes/microlink) | `a415d646` | Teardown UAF: `microlink_stop()`/`destroy()` didn't join worker tasks before freeing context; replaces "sleep 3s and hope" with a real per-task liveness bitmask + reap-orphans path | 1 |
| 3 | [#22](https://github.com/fugo101/microlink/issues/22) (done) | [`cplewes/microlink`](https://github.com/cplewes/microlink) | `7120dfa4` | Three crash sites triggered by captive-portal DNS failures: DERP double-init dangling pointer, `ml_coord_task` touching a freed event group post-destroy, corrupt-state races during parallel teardown. Landed together with #21 in the same PR — `a415d646` (the commit for #21) supersedes most of `7120dfa4`'s NULL-guard approach with a proper liveness bitmask; only the DERP mbedTLS double-init fix from `7120dfa4` was a distinct, still-needed piece. | 1 |
| 4 | [#23](https://github.com/fugo101/microlink/issues/23) | [`cplewes/microlink`](https://github.com/cplewes/microlink) | `8f2ff39b`, `5c7303b4` | Captive-portal detection (HTTP 302 / TCP close) in `ml_coord.c`, backs off 5 min instead of retrying every 16s | 1 |
| 5 | [#24](https://github.com/fugo101/microlink/issues/24) | [`cplewes/microlink`](https://github.com/cplewes/microlink) | `9f6af750` | Yield 1 tick per peer in `disco_periodic_probes` — prevents starving other same-core tasks on large tailnets | 1 |
| 6 | [#25](https://github.com/fugo101/microlink/issues/25) | [`cplewes/microlink`](https://github.com/cplewes/microlink) | `b9636816` | DERP TLS session resumption (skip full ECDHE handshake on reconnect, ~7.5s→sub-second), connect timeout 10s→25s, per-peer exponential backoff on direct-path upgrade probes | 1 |
Expand Down
25 changes: 23 additions & 2 deletions components/microlink/include/microlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,18 +143,39 @@ esp_err_t microlink_rebind(microlink_t *ml);
/**
* @brief Stop and disconnect from Tailscale
* @param ml Handle
* @return ESP_OK on success
* @return ESP_OK once every worker task has exited,
* ESP_ERR_TIMEOUT if one or more were still running when the join
* window expired (the instance is still safe to destroy — see
* microlink_destroy()).
*
* Gracefully shuts down all tasks and closes connections.
* Sets the shutdown request and waits for the worker tasks to acknowledge it
* by exiting. A worker wedged in a DNS lookup or TLS handshake on a captive
* network can outlast the window; that is reported, not fatal.
*/
esp_err_t microlink_stop(microlink_t *ml);

/**
* @brief Destroy MicroLink instance and free all resources
* @param ml Handle (NULL-safe)
*
* If a worker task outlived microlink_stop()'s join window the instance is
* NOT freed here — freeing it would be a use-after-free the moment that task
* touched the context again. It is parked instead, and released by a later
* microlink_reap_orphans() / microlink_init() / microlink_destroy() call once
* the task has actually exited. The caller drops its handle either way.
*/
void microlink_destroy(microlink_t *ml);

/**
* @brief Release any instance whose free microlink_destroy() had to defer
*
* Called automatically by microlink_init() and microlink_destroy(). Exposed
* for applications that tear MicroLink down and then stay idle for a while
* and want the memory back sooner. Safe to call from any task at any time;
* it frees nothing that is still in use.
*/
void microlink_reap_orphans(void);

/**
* @brief Get current connection state
*/
Expand Down
40 changes: 40 additions & 0 deletions components/microlink/include/microlink_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,24 @@ typedef struct {
#define ML_EVT_DERP_RECONNECT BIT7
#define ML_EVT_DERP_CONNECT_REQ BIT8

/* ============================================================================
* Worker Task Liveness
*
* One bit per task created by microlink_start(). The bit is set BEFORE
* xTaskCreate and cleared by the task itself, as its very last action, via
* ml_task_exit(). A zero mask is therefore *proof* that no worker can touch
* the context again — which is what microlink_destroy() gates the free on.
* See the "Teardown contract" comment at the top of microlink.c.
* Adapted from cplewes/microlink@a415d646.
* ========================================================================== */

#define ML_TASK_BIT_NET_IO (1u << 0)
#define ML_TASK_BIT_DERP_TX (1u << 1)
#define ML_TASK_BIT_COORD (1u << 2)
#define ML_TASK_BIT_WG_MGR (1u << 3)
#define ML_TASK_BIT_ALL (ML_TASK_BIT_NET_IO | ML_TASK_BIT_DERP_TX | \
ML_TASK_BIT_COORD | ML_TASK_BIT_WG_MGR)

/* ============================================================================
* Queue Message Types
* ========================================================================== */
Expand Down Expand Up @@ -347,6 +365,11 @@ struct microlink_s {
TaskHandle_t coord_task;
TaskHandle_t wg_mgr_task;

/* ML_TASK_BIT_* mask of workers that are still running. Set before
* xTaskCreate, cleared by each task's ml_task_exit(). Touched from both
* cores — access only through the __atomic helpers in microlink.c. */
uint32_t tasks_running;

/* Queues */
QueueHandle_t derp_tx_queue; /* -> derp_tx task */
QueueHandle_t disco_rx_queue; /* net_io -> wg_mgr */
Expand Down Expand Up @@ -454,6 +477,23 @@ struct microlink_s {
* Internal Function Declarations (per-module)
* ========================================================================== */

/* microlink.c — worker task lifecycle helpers */

/** Current ML_TASK_BIT_* mask of running workers (0 = all joined). */
uint32_t ml_tasks_running(const microlink_t *ml);

/** True once ML_EVT_SHUTDOWN_REQUEST is set (or the context is gone).
* Call this from anywhere a worker can block for more than a moment — a
* DNS lookup, a TLS handshake retry, a backoff delay — so teardown does not
* have to wait out the full network timeout. */
bool ml_shutdown_pending(microlink_t *ml);

/** Terminate the calling worker task. Clears its ML_TASK_BIT_* and never
* returns. THE CONTEXT MUST NOT BE TOUCHED AFTER CALLING THIS — clearing the
* bit is what licenses microlink_destroy() to free ml, its queues and its
* event group. */
void ml_task_exit(microlink_t *ml, uint32_t task_bit);

/* ml_net_io.c */
void ml_net_io_task(void *arg);

Expand Down
Loading