AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 76979096
Accepted
444RS
444RS
Asked: 2023-08-26 00:41:38 +0800 CST2023-08-26 00:41:38 +0800 CST 2023-08-26 00:41:38 +0800 CST

Qual é o problema neste código Rust inseguro para que funcione no Windows, mas não no Ubuntu?

  • 772

Olá, sei que o código poderia ser totalmente escrito sem nenhum código inseguro, mas estou pesquisando e aprendendo como as coisas funcionam "nos bastidores".

Voltando ao assunto , escrevi um código Rust inseguro que, na minha opinião, deve funcionar sem problemas.

Esta é a definição:

pub struct Container {
    inner: Pin<Box<String>>,
    half_a: *const str,
    half_b: *const str,
}

impl Container {
    const SEPARATOR: char = '-';

    pub fn new(input: impl AsRef<str>) -> Option<Self> {
        let input = input.as_ref();
        if input.is_empty() {
            return None
        }

        // Making sure the value is never moved in the memory
        let inner = Box::pin(input.to_string());

        let separator_index = inner.find(Container::SEPARATOR)?;
        let inner_ref = &**inner;

        let half_a = &inner_ref[0..separator_index];
        let half_b = &inner_ref[separator_index+1..];

        // Check if the structure definition is met (populated values + only one separator)
        if half_a.is_empty() || half_b.is_empty() || half_b.contains(Container::SEPARATOR) {
            return None;
        }

        Some(Self {
            half_a: half_a as *const str,
            half_b: half_b as *const str,
            inner,
        })
    }
    
    pub fn get_half_a(&self) -> &str {
        unsafe {
            &*self.half_a
        }
    }

    pub fn get_half_b(&self) -> &str {
        unsafe {
            &*self.half_b
        }
    }
}

Em resumo, ele aceita qualquer entrada que possa ser representada como uma referência str, cria um clone fixado da entrada no heap, obtém endereços que apontam para ambas as metades desse valor e retorna isso como uma estrutura.

Agora, quando faço testes:

let valid = Container::new("first-second").unwrap();
assert_eq!(valid.get_half_a(), "first");
assert_eq!(valid.get_half_b(), "second");

Deve funcionar sem pânico e é isso que acontece no Windows. Ele compila e roda sem problemas várias vezes, mas quando é executado no Ubuntu recebo um erro mostrando que os endereços não apontam mais para um local válido na memória:

 thread 'tests::types::container' panicked at 'assertion failed: `(left == right)`
  left: `"�K\u{13}϶"`,
 right: `"first"`', research/src/tests/types.rs:77:5

Qual poderia ser o problema aqui? Perdi algo? Estou executando este código como ação do GitHub com o seguinte sinalizador runs-on: ubuntu-latest.

Aqui está um URL para o playground mostrando que este código é executado sem problemas: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d36b19de4d0fa05340191f5107029d75

Não esperava problemas ao executar este código em um sistema operacional diferente.

windows
  • 1 1 respostas
  • 72 Views

1 respostas

  • Voted
  1. Best Answer
    drewtato
    2023-08-26T03:37:29+08:002023-08-26T03:37:29+08:00

    Mudar Box<String>paraBox<str> , o que não deve afetar a solidez, aciona o MIRI.

    error: Undefined Behavior: trying to retag from <2563> for SharedReadOnly permission at alloc890[0x0], but that tag does not exist in the borrow stack for this location
      --> src/main.rs:41:18
       |
    41 |         unsafe { &*self.half_a }
       |                  ^^^^^^^^^^^^^
       |                  |
       |                  trying to retag from <2563> for SharedReadOnly permission at alloc890[0x0], but that tag does not exist in the borrow stack for this location
       |                  this error occurs as part of retag at alloc890[0x0..0x5]
       |
       = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
       = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
    help: <2563> was created by a SharedReadOnly retag at offsets [0x0..0x5]
      --> src/main.rs:34:21
       |
    34 |             half_a: half_a as *const str,
       |                     ^^^^^^
    help: <2563> was later invalidated at offsets [0x0..0xc] by a Unique retag (of a reference/box inside this compound value)
      --> src/main.rs:36:13
       |
    36 |             inner,
       |             ^^^^^
       = note: BACKTRACE (of the first span):
       = note: inside `Container::get_half_a` at src/main.rs:41:18: 41:31
    note: inside `main`
      --> src/main.rs:51:16
       |
    51 |     assert_eq!(valid.get_half_a(), "first");
       |                ^^^^^^^^^^^^^^^^^^
    

    Isso vem de Box, que não pode ter alias. Embora normalmente seja bom derivar ponteiros de Box, quando você move o Box(retornando Container), Rust não sabe mais que o Boxteve ponteiros derivados dele e assume que os acessos através dos ponteiros são inválidos devido ao alias.

    É por isso que o MIRI é acionado. No entanto, não tenho certeza do que torna esse comportamento indefinido. Os resultados do seu teste sugerem que sim, mas não podem dizer por quê. Meu palpite é que Rust decide que innerpode ser descartado assim que newretornar, já que é garantido que seja único. Pode até otimizar a alocação para nunca gravar nenhum dado (o ponteiro, o comprimento e a capacidade da Stringsua versão), uma vez que esses dados nunca são lidos, o que explicaria o seu erro de tempo de execução.

    Você pode corrigir isso armazenando apenas ponteiros e implementando Drop. (Parque infantil)

    pub struct Container {
        inner: *mut str,
        half_a: *const str,
        half_b: *const str,
    }
    
    impl Drop for Container {
        fn drop(&mut self) {
            // SAFETY: Nothing references this value since it is being dropped,
            // and `half_a` and `half_b` are never read after this.
            unsafe { drop(Box::from_raw(self.inner)) }
        }
    }
    

    Eu não acho que Pinfaça nada pela solidez aqui. Piné mais usado para lidar com interfaces públicas. Contanto que você não distribua nenhuma &mutreferência a inner, não há nada contra o que se proteger. Embora você possa querer isso para garantias internas, suas garantias reais são mais fortes do que Pinporque você não pode usar o valor de forma alguma.

    • 3

relate perguntas

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    destaque o código em HTML usando <font color="#xxx">

    • 2 respostas
  • Marko Smith

    Por que a resolução de sobrecarga prefere std::nullptr_t a uma classe ao passar {}?

    • 1 respostas
  • Marko Smith

    Você pode usar uma lista de inicialização com chaves como argumento de modelo (padrão)?

    • 2 respostas
  • Marko Smith

    Por que as compreensões de lista criam uma função internamente?

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Marko Smith

    java.lang.NoSuchMethodError: 'void org.openqa.selenium.remote.http.ClientConfig.<init>(java.net.URI, java.time.Duration, java.time.Duratio

    • 3 respostas
  • Marko Smith

    Por que 'char -> int' é promoção, mas 'char -> short' é conversão (mas não promoção)?

    • 4 respostas
  • Marko Smith

    Por que o construtor de uma variável global não é chamado em uma biblioteca?

    • 1 respostas
  • Marko Smith

    Comportamento inconsistente de std::common_reference_with em tuplas. Qual é correto?

    • 1 respostas
  • Marko Smith

    Somente operações bit a bit para std::byte em C++ 17?

    • 1 respostas
  • Martin Hope
    fbrereto Por que a resolução de sobrecarga prefere std::nullptr_t a uma classe ao passar {}? 2023-12-21 00:31:04 +0800 CST
  • Martin Hope
    比尔盖子 Você pode usar uma lista de inicialização com chaves como argumento de modelo (padrão)? 2023-12-17 10:02:06 +0800 CST
  • Martin Hope
    Amir reza Riahi Por que as compreensões de lista criam uma função internamente? 2023-11-16 20:53:19 +0800 CST
  • Martin Hope
    Michael A formato fmt %H:%M:%S sem decimais 2023-11-11 01:13:05 +0800 CST
  • Martin Hope
    God I Hate Python std::views::filter do C++20 não filtrando a visualização corretamente 2023-08-27 18:40:35 +0800 CST
  • Martin Hope
    LiDa Cute Por que 'char -> int' é promoção, mas 'char -> short' é conversão (mas não promoção)? 2023-08-24 20:46:59 +0800 CST
  • Martin Hope
    jabaa Por que o construtor de uma variável global não é chamado em uma biblioteca? 2023-08-18 07:15:20 +0800 CST
  • Martin Hope
    Panagiotis Syskakis Comportamento inconsistente de std::common_reference_with em tuplas. Qual é correto? 2023-08-17 21:24:06 +0800 CST
  • Martin Hope
    Alex Guteniev Por que os compiladores perdem a vetorização aqui? 2023-08-17 18:58:07 +0800 CST
  • Martin Hope
    wimalopaan Somente operações bit a bit para std::byte em C++ 17? 2023-08-17 17:13:58 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve