Issue #19153 has been reported by cpinto (Celso Pinto).
----------------------------------------
Bug #19153: Since 2.7.7 CGI::Cookie raises ArgumentError when cookie domains is prefixed with a dot
https://bugs.ruby-lang.org/issues/19153
* Author: cpinto (Celso Pinto)
* Status: Open
* Priority: Normal
* ruby -v: ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5) [arm64-darwin22]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
The rspec tests of our Rails app started failing with an ArgumentError after upgrading to 2.7.7. On inspection, the issue seems to be caused by CGI::Cookie.domain=:
```
def domain=(str)
if str and ((str = str.b).bytesize > 255 or !DOMAIN_VALUE_RE.match?(str))
raise ArgumentError, "invalid domain: #{str.dump}"
end
@domain = str
end
```
Setting a breakpoint:
0> str
=> ".example.com"
0> DOMAIN_VALUE_RE
=> /\A(?<label>(?!-)[-A-Za-z0-9]+(?<!-))(?:\.\g<label>)*\z/
0> DOMAIN_VALUE_RE.match?(str)
=> false
0> DOMAIN_VALUE_RE.match?('example.com')
=> true
--
https://bugs.ruby-lang.org/
Issue #19179 has been reported by kjtsanaktsidis (KJ Tsanaktsidis).
----------------------------------------
Feature #19179: Support parsing SCM_CRED(ENTIALS) messages from ancillary messages
https://bugs.ruby-lang.org/issues/19179
* Author: kjtsanaktsidis (KJ Tsanaktsidis)
* Status: Open
* Priority: Normal
----------------------------------------
## Background
Linux and FreeBSD support processes at either end of a unix socket identifying themselves to the other party by passing an ancillary message of type `SCM_CREDENTIALS` (Linux) or `SCM_CREDS` (FreeBSD). The socket library contains code to parse these ancillary messages, but the only way this is exposed into Ruby code is by the `Socket::AncillaryData#inspect` method - e.g.
```
# On Linux
irb(main):002:0> s1, s2 = UNIXSocket.pair
=> [#<UNIXSocket:fd 5>, #<UNIXSocket:fd 6>]
irb(main):004:0> s2.setsockopt Socket::SOL_SOCKET, Socket::SO_PASSCRED, 1
=> 0
# struct ucred on Linux is (32-bit signed) pid_t, followed by (32-bit unsigned) uid_t, followed by
# (32-bit unsigned) gid_t
irb(main):008:0> ancdata = [Process.pid, Process.uid, Process.gid].pack("lLL")
=> "\x1ET\x05\x00\xE8\x03\x00\x00\xE8\x03\x00\x00"
# Socket::AncillaryData knows how to unmarshal the data into struct ucred
irb(main):010:0> ancmsg = Socket::AncillaryData.new(Socket::AF_UNIX, Socket::SOL_SOCKET, Socket::SCM_CRE
DENTIALS, ancdata)
=> #<Socket::AncillaryData: UNIX SOCKET CREDENTIALS pid=349214 uid=1000 gid=1000 (ucred)>
irb(main):011:0> s1.sendmsg "hi", 0, nil, ancmsg
=> 2
# ancillary message can be passed through
irb(main):012:0> _, _, _, recvanc = s2.recvmsg; recvanc
=> #<Socket::AncillaryData: UNIX SOCKET CREDENTIALS pid=349214 uid=1000 gid=1000 (ucred)>
```
On Linux, at least, a suitably privileged process can send any value through for the pid, uid, or gid, but the kernel will reject attempts by unprivileged processes to forge credentials in this way. So SCM_CREDENTIALS messages can be useful for certain systems programming tasks.
A somewhat wider array of operating systems support querying the identity of the other side of a socket using a socket option, variously `SO_PEERCRED` (Linux, OpenBSD) or `LOCAL_PEERCRED` (FreeBSD, MacOS). Again, the socket library is able to unmarshal the socket data into the correct structure on these various systems, but it's only exposed to Ruby code via `#inspect` - e.g.
```
irb(main):002:0> s1, s2 = UNIXSocket.pair
=> [#<UNIXSocket:fd 5>, #<UNIXSocket:fd 6>]
irb(main):014:0> s1.getsockopt Socket::SOL_SOCKET, Socket::SO_PEERCRED
=> #<Socket::Option: UNIX SOCKET PEERCRED pid=349214 euid=1000 egid=1000 (ucred)>
```
Ruby _does_ however support e.g. `BasicSocket#getpeereid`, which could use `SO_PEERCRED` etc under the hood - so getting the uid/gid data is not totally impossible. I believe getting the pid is though.
```
irb(main):016:0> s1.getpeereid
=> [1000, 1000]
```
## My proposal
I believe we should implement the following:
* `Socket::Credentials` - this would be a struct which can contain all the various platform-specific pieces of credential info that can be transferred over a socket, such as uid, gid, pid, euid, egid, and group list.
* `Socket::AncillaryData#credentials` - this would parse an `SCM_CREDS` or `SCM_CREDENTIALS` ancillary data message into the appropriate platform-specific struct, and return a `Socket::Credentials` instance containing that data. This would be analogous to `Socket::AncillaryData#int`; a method for interpreting the ancillary data in a certain form.
* `Socket::Option#credentials` - This would parse a `SO_PEERCRED` or `LOCAL_PEERCRED` socket option response into the appropriate platform-specific struct, and return a `Socket::Credentials` instance containing that data. Again, this would be analogous to `Socket::Option#int`.
The existing `struct ucred`/`struct xucred`/`struct sockpeercred`/`struct cmsgcred` parsing code (used only for `#inspect` output) would be moved into `Socket::Credentials`, and `Socket::AncillaryData#inspect`/`Socket::Option#inspect` would be implemented in terms of `Socket::Credentials`.
This would nicely wrap a lot of parsing work that Ruby is already doing, into an API which allows Ruby code to take advantage of it.
## Use-cases
My motivation for designing this feature came about whilst I was experimenting with some ideas for Ruby profilers. I wanted to allow a CLI tool to ask a Ruby process to start profiling itself by sending a message on a unix socket. Alongside the message, it would send a file descriptor which was the result of calling `perf_event_open(2)` in the CLI tool. In order to call `perf_event_open(2)`, the CLI tool would need to be privileged. I also wanted the Ruby process to authenticate the request and make sure it came from the same UID that it was running as. Calling `BasicSocket#getpeereuid` would reveal the remote process to be running as UID 0, (or perhaps even some other UID, with sufficient ambient capabilities to call `perf_event_open`). Instead, I decided to make the CLI tool send a `SCM_CREDENTIALS` message containing the uid of the process to be profiled; that way, the kernel does all the policy checking on whether or not this is actually allowed, and the Ruby process receiving the message just needs to check if `uid == Process.getuid`.
I think, on Linux at least, that this feature will be useful for any kind of communication/authentication scheme between privileged & unprivileged processes over unix sockets.
## My implementation
I have an implementation of roughly this in this pull request: https://github.com/ruby/ruby/pull/6822
Thanks!
--
https://bugs.ruby-lang.org/
Issue #19272 has been reported by zverok (Victor Shepelev).
----------------------------------------
Feature #19272: Hash#merge: smarter protocol depending on passed block arity
https://bugs.ruby-lang.org/issues/19272
* Author: zverok (Victor Shepelev)
* Status: Open
* Priority: Normal
----------------------------------------
Usage of `Hash#merge` with a "conflict resolution block" is almost always clumsy: due to the fact that the block accepts `|key, old_val, new_val|` arguments, and many trivial usages just somehow sum up old and new keys, the thing that should be "intuitively trivial" becomes longer than it should be:
```ruby
# I just want a sum!
{apples: 1, oranges: 2}.merge(apples: 3, bananas: 5) { |_, o, n| o + n }
# I just want a group!
{words: %w[I just]}.merge(words: %w[want a group]) { |_, o, n| [*o, *n] }
# I just want to unify flags!
{'file1' => File::READABLE, 'file2' => File::READABLE | File::WRITABLE}
.merge('file1' => File::WRITABLE) { |_, o, n| o | n }
# ...or, vice versa:
{'file1' => File::READABLE, 'file2' => File::READABLE | File::WRITABLE}
.merge('file1' => File::WRITABLE, 'file2' => File::WRITABLE) { |_, o, n| o & n }
```
It is especially noticeable in the last two examples, but the _usual_ problem is there are too many "unnecessary" punctuation, where the essential might be lost.
There are proposals like #19148, which struggle to define _another_ method (what would be the name? isn't it just merging?)
But I've been thinking, can't the implementation be chosen based on the arity of the passed block?.. Prototype:
```ruby
class Hash
alias old_merge merge
def merge(other, &block)
return old_merge(other) unless block
if block.arity.abs != 2
old_merge(other, &block)
else
old_merge(other) { |_, o, n| block.call(o, n) }
end
end
end
{apples: 1, oranges: 2}.merge(apples: 3, bananas: 5, &:+)
#=> {:apples=>4, :oranges=>2, :bananas=>5}
{words: %w[I just]}.merge(words: %w[want a group], &:concat)
=> {:words=>["I", "just", "want", "a", "group"]}
{'file1' => File::READABLE, 'file2' => File::READABLE | File::WRITABLE}
.merge('file1' => File::WRITABLE, &:|)
# => {"file1"=>5, "file2"=>5}
{'file1' => File::READABLE, 'file2' => File::READABLE | File::WRITABLE}
.merge('file1' => File::WRITABLE, 'file2' => File::WRITABLE, &:&)
# => {"file1"=>0, "file2"=>4}
# If necessary, old protocol still works:
{apples: 1, oranges: 2}.merge(apples: 3, bananas: 5) { |k, o, n| k == :apples ? 0 : o + n }
# => {:apples=>0, :oranges=>2, :bananas=>5}
```
As far as I can remember, Ruby core doesn't have methods like this (that change implementation depending on arity of passed callable), but I think I saw this approach in other languages. Can't remember particular examples, but always found this idea appealing.
--
https://bugs.ruby-lang.org/
Issue #19172 has been reported by ivoanjo (Ivo Anjo).
----------------------------------------
Bug #19172: `ruby_thread_has_gvl_p` is innacurate sometimes -- document or change?
https://bugs.ruby-lang.org/issues/19172
* Author: ivoanjo (Ivo Anjo)
* Status: Open
* Priority: Normal
* ruby -v: (All Ruby versions)
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
Howdy 👋! I work for Datadog [on the ddtrace gem](https://github.com/DataDog/dd-trace-rb) and I found a... sharp edge on the internal `ruby_thread_has_gvl_p` API.
I am aware that `ruby_thread_has_gvl_p` is documented an experimental API that is exported as a symbol but not present on the VM include files.
### Background
In the ddtrace profiling component, we setup a signal handler and then periodically send SIGPROF signals to try to interrupt the running Ruby thread (e.g. the thread that is holding the global VM lock or equivalent).
In the signal handler, we need to perform some API calls which are not safe to do without the GVL. So we need to check if the signal handler got called in the thread that has the GVL.
### The issue
```ruby
int
ruby_thread_has_gvl_p(void)
{
rb_thread_t *th = ruby_thread_from_native();
if (th && th->blocking_region_buffer == 0) {
return 1;
}
else {
return 0;
}
}
```
In its current implementation, `ruby_thread_has_gvl_p` only checks if the thread has a `blocking_region_buffer` or not. Unfortunately, this means that when called from a thread that lost the GVL but not due to blocking (e.g. via `rb_thread_schedule()`), it can still claim that a thread is holding the GVL when that is not the case.
I ran into this issue in https://github.com/DataDog/dd-trace-rb/pull/2415, and needed to find a workaround.
### Next steps
Since this is an internal VM API, I'm not sure you'd want to change the current behavior, so I was thinking of perhaps two options:
* Is it worth changing `ruby_thread_has_gvl_p` to be accurate in the case I've listed?
* If not, would you accept a PR to document its current limitations, so that others don't run into the same issue I did?
--
https://bugs.ruby-lang.org/
Issue #19154 has been reported by fxn (Xavier Noria).
----------------------------------------
Bug #19154: Specify require and autoload guarantees in ractors
https://bugs.ruby-lang.org/issues/19154
* Author: fxn (Xavier Noria)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.0preview3 (2022-11-27) [x86_64-darwin22]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
Given a file `c.rb`:
```ruby
class C
end
```
the following script:
```ruby
r1 = Ractor.new do
require './c.rb'
end
r2 = Ractor.new do
require './c.rb'
end
r1.take
r2.take
```
raises:
```
% ruby -v foo.rb
ruby 3.2.0preview3 (2022-11-27) [x86_64-darwin22]
foo.rb:1: warning: Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues.
#<Thread:0x000000010fee2928 run> terminated with exception (report_on_exception is true):
#<Thread:0x00000001102acfe0 run> terminated with exception (report_on_exception is true):
<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:164:in `ensure in require': can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError)
from <internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:167:in `require'
from foo.rb:6:in `block in <main>'
<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:37:in `require'<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:164:in `ensure in require': : can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError)
from <internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:167:in `require'
can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError) from foo.rb:2:in `block in <main>'
<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:37:in `require': can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError)
from foo.rb:2:in `block in <main>'
from foo.rb:6:in `block in <main>'
<internal:ractor>:698:in `take': thrown by remote Ractor. (Ractor::RemoteError)
from foo.rb:9:in `<main>'
<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:164:in `ensure in require': can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError)
from <internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:167:in `require'
from foo.rb:2:in `block in <main>'
<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:37:in `require': can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError)
from foo.rb:2:in `block in <main>'
```
Would it be possible to have documentation about their interaction?
This is important also to understand autoloading within ractors, since constant references may trigger `require` calls.
--
https://bugs.ruby-lang.org/
Issue #19289 has been reported by Eregon (Benoit Daloze).
----------------------------------------
Bug #19289: RbConfig::CONFIG["STRIP"] should keep `rb_abi_version` and `rb_abi_version` should always be part of Ruby
https://bugs.ruby-lang.org/issues/19289
* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
From https://github.com/grpc/grpc/pull/31970 and https://github.com/redis-rb/redis-client/issues/58
First, I think we could add `-K rb_abi_version` to `RbConfig::CONFIG["STRIP"]` so it's automatically kept if `RbConfig::CONFIG["STRIP"]` is used (and that should be used if one strips any native extension).
Second, I think it would be much better if the symbol is kept also for releases.
The check could be kept too for safety (e.g., it can detect Ruby 3.3.0 gems used by Ruby 3.2.0), the value of `rb_abi_version` would just be the same as `RbConfig::CONFIG["ruby_version"]`, i.e., 3.2.0 for Ruby 3.2.x.
Any difference between dev and release builds is a risk of not properly testing the release, and there is proof here that removing the symbol in releases causes troubles.
Doing both of these would avoid complex and brittle logic upstream as in grpc and redis-client to deal with the new symbol.
cc @nobu @peterzhu2118
--
https://bugs.ruby-lang.org/
Issue #19286 has been reported by matsuda (Akira Matsuda).
----------------------------------------
Bug #19286: What should kwargs' arity be?
https://bugs.ruby-lang.org/issues/19286
* Author: matsuda (Akira Matsuda)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.3.0dev (2022-12-28T16:43:05Z master cada537040) +YJIT [arm64-darwin21]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Hello, guys. It's time for a quick Ruby quiz.
Q: What is this method's arity?
def f(a:, b:)
end
It requires two arguments, hence it should be 2?
Or if we call this method with one argument, the error message says "wrong number of arguments (given 1, expected 0; required keywords: a, b) (ArgumentError)", which means the arity is 0, maybe?
A: The answer is,
$ all-ruby -e 'p method(def f(a:, b:) end).arity'
ruby-2.1.0-preview1 0
...
ruby-2.1.0 0
ruby-2.1.1 -1
ruby-2.1.2 1
...
ruby-3.1.0 1
it's been 1 since 2.1.2. But why 1? Why not 2 nor 0?
I asked this question to the ruby-core people, and ko1's answer was that even he has no idea what the number 1 means.
![](random_-_ruby-lang_-_Slack.png)
So I thought it'd be worth asking this question here.
---Files--------------------------------
random_-_ruby-lang_-_Slack.png (40.1 KB)
--
https://bugs.ruby-lang.org/