-
Notifications
You must be signed in to change notification settings - Fork 137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
close/1 improperly handles user_input
and user_output
(and other stream weirdness)
#2806
Comments
The example above actually runs into the issue of #[test]
#[cfg_attr(miri, ignore)]
fn close_memory_user_output_stream() {
let mut machine = MachineBuilder::new()
.with_streams(StreamConfig::in_memory())
.build();
let results = machine
.run_query("\\+ \\+ (current_output(Stream), close(Stream)), write(user_output, hello).")
.collect::<Vec<_>>();
assert_eq!(results.len(), 1);
assert!(results[0].is_ok());
}
Here's the second test case that I'm using: #[test]
#[cfg_attr(miri, ignore)]
fn close_memory_user_output_stream_twice() {
let mut machine = MachineBuilder::new()
.with_streams(StreamConfig::in_memory())
.build();
let results = machine
.run_query("\\+ \\+ (current_output(Stream), close(Stream), close(Stream)).")
.collect::<Vec<_>>();
assert_eq!(results.len(), 1);
assert!(results[0].is_ok());
} |
I've also unearthed another issue with ?- open("README.md", read, S, [alias(readme)]), open(stream(S), read, _, [alias(not_readme)]), close(S).
S = '$dropped_value'.
% From now on, we have a dead entry in `stream_aliases`, which points to a dropped stream.
% We can't read from it, since the underlying file handle was dropped, but we can access its options:
?- stream_property(readme, file_name(File)).
File = 'README.md', unexpected.
error(existence_error(stream,readme),get_code/2). % expected, but not found
?- get_code(readme, C).
error(syntax_error(input_output_error),get_code/2), unexpected.
error(existence_error(stream,readme),get_code/2). % expected, but not found Removing the second I'm not sure if this counts as a use-after-free, but I know for sure that it's a bad idea to read from that stream. I also think that |
Another one I'm now handling on my patch branch: :- open("README.md", read, S), open(stream(S), read, _, [alias(user_output)]), close(S).
?- write(user_output, hello).
error(existence_error(stream,user_output),write_term/0), unexpected.
true. % Expected, but not found |
user_input
and user_output
user_input
and user_output
(and other stream weirdness)
I noticed that the example from #1070 now panics (it corrupts an ?- current_output(S0), open(stream(S0), write, S, [type(binary)]). So far re-opening a stream only modifies its properties, but I wonder if we shouldn't instead return a new, fresh stream handle, that way we don't break stuff? This would require rc-ing the streams, though, and causes headaches around stuff like mixing |
Creating a new stream handle for this is equally OK, or in fact even better. In #1070, I only did the minimum that was necessary to implement the use case of outputting binary data on the terminal, and then halting. |
@adri326: An existing application where this functionality is used is Enscryerypt, to emit the decrypted data verbatim, without translation to UTF-8 encoding. |
This last one doesn't seem to be related to close/1, so I'll open as a new issue |
The logic of
close/1
seems to have been put together hastily:machine_st.indices.streams
, no matter whatuser_input
oruser_output
streams, put it backStream::Stdin
,Stream::Stdout
orStream::Stderr
, then remove it frommachine_st.indices.stream_aliases
If
user_output
happens to not beStream::Stdout
(in the following test, by constructingMachine
with thein_memory()
preset), thencurrent_output(S), close(S)
will removeuser_output
frommachine_st.indices.stream_aliases
, but not frommachine_st.indices.streams
ormachine_st.user_output
. From then on things break downstream.This is one of the rare places whereIndexStore::streams
differ fromIndexStore::stream_aliases
(the only other one being'$first_stream'/1
and'$next_stream'/1
, whose behavior could be implemented in terms ofstream_aliases.values()
), so I'm wondering if there really is a point in having both of these, since'$next_stream'/1
expectsstreams
to contain all non-null streams found instream_aliases.values()
.Nevermind about the thought that
IndexStore::streams
is not needed.The text was updated successfully, but these errors were encountered: