Safety-critical code: MISRA C e certificações

1. Fundamentos de Código Safety-Critical

Sistemas safety-critical são aqueles cuja falha pode resultar em consequências catastróficas: perda de vidas humanas, danos ambientais severos ou prejuízos financeiros irreparáveis. Exemplos clássicos incluem sistemas de freios antibloqueio (ABS) em automóveis, controles de voo em aeronaves, marcapassos cardíacos e sistemas de controle de usinas nucleares.

Nesses domínios, o software não pode simplesmente "falhar e reiniciar". A confiabilidade deve ser garantida por processos rigorosos de engenharia. O ciclo de vida de desenvolvimento mais adotado é o V-Model, que estabelece uma correspondência direta entre fases de especificação e testes. A norma genérica IEC 61508 serve como base para derivadas específicas como ISO 26262 (automotivo) e IEC 62304 (médico).

As características fundamentais de código safety-critical incluem:
- Previsibilidade absoluta de comportamento
- Ausência de comportamento indefinido
- Rastreabilidade entre requisitos e implementação
- Cobertura de teste exaustiva (MC/DC para níveis mais críticos)

2. Introdução ao MISRA C

O MISRA C (Motor Industry Software Reliability Association) surgiu em 1998 como um conjunto de diretrizes para código C seguro na indústria automotiva britânica. Desde então, evoluiu para se tornar padrão em múltiplos setores críticos.

As principais versões são:
- MISRA C:1998 — foco em C90, 127 regras
- MISRA C:2004 — expansão para 141 regras, introdução de diretrizes
- MISRA C:2012 — alinhamento com C99, 143 regras + 16 diretrizes
- MISRA C:2023 — suporte a C11/C17, novas regras para segurança funcional

A estrutura do MISRA C classifica as regras em três categorias:
- Obrigatórias (Required) — devem ser seguidas sem exceção
- Recomendadas (Advisory) — boas práticas, mas permitem desvios justificados
- Diretrizes — orientações gerais sobre processo e documentação

O escopo cobre desde C90 até C18, com a versão 2023 já incorporando suporte a C23 parcialmente. É crucial notar que MISRA C não é um padrão de linguagem, mas um conjunto de restrições de uso seguro.

3. Regras Essenciais do MISRA C para Segurança

Proibição de Comportamento Indefinido

A regra mais fundamental do MISRA C é eliminar qualquer fonte de comportamento indefinido. Exemplos críticos:

Ponteiros nulos (Regra 11.5):

/* Não-conforme */
void processar(int *ptr) {
    *ptr = 10;  /* Comportamento indefinido se ptr for NULL */
}

/* Conforme */
void processar(int *ptr) {
    if (ptr != NULL) {
        *ptr = 10;
    }
}

Estouro de buffer (Regra 18.1):

/* Não-conforme */
int buffer[10];
buffer[10] = 42;  /* Acesso fora dos limites */

/* Conforme */
#define TAMANHO_BUFFER 10
int buffer[TAMANHO_BUFFER];
if (indice < TAMANHO_BUFFER) {
    buffer[indice] = 42;
}

Restrições de Fluxo de Controle

MISRA C restringe severamente construções que prejudicam a análise estática:

  • Regra 15.1: goto só permitido para forward jumps em tratamento de erros
  • Regra 15.2: break e continue proibidos em loops aninhados
  • Regra 17.2: Recursão proibida (dificulta verificação de terminação)

Gerenciamento de Memória

Regra 21.3: Alocação dinâmica (malloc, free) é proibida em sistemas safety-critical. A razão é clara: fragmentação de memória, vazamentos e comportamento não determinístico são inaceitáveis.

/* Conforme - uso de memória estática */
#define MAX_CLIENTES 100
typedef struct {
    uint8_t id;
    uint16_t dados;
} Cliente;

static Cliente clientes[MAX_CLIENTES];
static uint8_t num_clientes = 0;

4. Boas Práticas de Codificação para Certificação

Tipos de Dados Explícitos

Use os tipos de stdint.h para garantir portabilidade e limites conhecidos:

#include <stdint.h>

/* Recomendado MISRA */
int32_t velocidade;      /* Exatamente 32 bits com sinal */
uint16_t temperatura;    /* Exatamente 16 bits sem sinal */

/* Evitado por MISRA */
int valor;               /* Tamanho dependente da plataforma */

Prevenção de Efeitos Colaterais

A Regra 13.2 proíbe múltiplos efeitos colaterais em uma expressão:

/* Não-conforme - comportamento indefinido */
int resultado = ++x + x++;

/* Conforme */
x++;
int resultado = x + x;

Documentação de Desvios

Quando uma regra MISRA é violada intencionalmente, é obrigatório documentar:
1. A regra violada
2. A justificativa técnica
3. A análise de risco associada
4. A aprovação do revisor de segurança

5. Ferramentas de Análise Estática e MISRA

Ferramentas de análise estática são essenciais para verificação de conformidade MISRA:

  • PC-lint / FlexeLint — uma das mais antigas e completas, suporta todas as versões MISRA
  • Coverity — análise profunda com detecção de fluxos de dados complexos
  • QAC (QA·C) — ferramenta especializada da Perforce para MISRA e certifição

A configuração típica envolve:
1. Selecionar o perfil MISRA adequado (ex: MISRA C:2012 Required)
2. Definir níveis de severidade (erro, warning, informação)
3. Tratar falsos positivos com supressão controlada e documentada

/* Exemplo de supressão documentada (PC-lint) */
/*lint -e(1234) Justificativa: regra violada intencionalmente por requisito de hardware */
uint16_t ler_sensor(void) {
    return *(volatile uint16_t*)0x4000;
}
/*lint +e(1234) */

6. Processos de Certificação e Normas Relacionadas

ISO 26262 (Automotivo)

Define níveis ASIL (Automotive Safety Integrity Level) de A a D:
- ASIL A: Leve (falha causa desconforto)
- ASIL D: Crítico (falha causa morte)

MISRA C é referenciado como "estado da arte" para alcançar ASIL B e superiores. Para ASIL D, é comum exigir 100% de conformidade com regras obrigatórias.

DO-178C (Aeroespacial)

Define níveis DAL (Design Assurance Level):
- DAL A: Falha catastrófica → requer cobertura MC/DC
- DAL D: Falha menor → testes funcionais suficientes

MISRA C é usado como guia de codificação, mas a DO-178C não exige explicitamente. A combinação MISRA + análise estática é prática comum para DAL A/B.

IEC 62304 (Dispositivos Médicos)

Classifica software em três classes:
- Classe A: Sem risco ao paciente
- Classe B: Risco não grave
- Classe C: Risco grave ou morte

Para classe C, MISRA C é fortemente recomendado como parte das técnicas de prevenção de erros.

7. Exemplos Práticos de Conformidade

Buffer Overflow: Não-conforme vs. Conforme

/* NÃO-CONFORME MISRA */
void copiar_dados(char *dest, char *src) {
    while (*src) {
        *dest++ = *src++;
    }
    *dest = '\0';
}

/* CONFORME MISRA (Regra 18.1 + 21.12) */
#include <string.h>
#define TAM_MAX 32

void copiar_dados_seguro(char dest[TAM_MAX], const char *src) {
    size_t len = strlen(src);
    if (len < TAM_MAX) {
        (void)memcpy(dest, src, len + 1);
    }
}

Loop Infinito Controlado (Regra 14.3)

/* Conforme - loop infinito com condição de saída explícita */
volatile bool sistema_ativo = true;

void loop_principal(void) {
    while (sistema_ativo) {
        processar_eventos();
    }
}

8. Desafios e Limitações do MISRA C

Curva de Aprendizado

Equipes acostumadas com C "livre" enfrentam resistência inicial. A adaptação pode levar de 3 a 6 meses para domínio pleno.

Trade-offs entre Segurança e Desempenho

Restrições como proibição de alocação dinâmica e recursão podem aumentar o uso de memória estática e complexidade do código. Em sistemas embarcados com recursos limitados, isso exige planejamento cuidadoso.

Futuro do MISRA C

A versão 2023 já incorpora suporte a C11/C17, incluindo:
- _Atomic (com restrições)
- _Static_assert
- Tipos bool nativos

A integração com C23 está em andamento, especialmente para:
- nullptr (melhor que NULL)
- Atributos padronizados ([[nodiscard]], [[maybe_unused]])
- Melhorias em constexpr

O desafio será equilibrar a adoção de novos recursos com a necessidade de estabilidade em sistemas certificados.


Referências