# fix crostini antigravity

> Source: <https://gist.github.com/MichaelWS/9e97f791db704f078dcf66d0dd9245ee>
> Published: 2026-05-22 12:53:34+00:00

patch_va39

      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      
Learn more about bidirectional Unicode characters

 
    Show hidden characters

#!/usr/bin/env python3

"""

VA39 patch for the Antigravity linux_arm64 binary on Chromebook/Crostini.

Scans instruction patterns to patch TCMalloc for a 39-bit

virtual address space instead of the default 48-bit, fixing 

the MmapAligned() crash.

"""

import
 
hashlib

import
 
shutil

import
 
struct

import
 
sys

from
 
pathlib
 
import
 
Path

# Adjust this path if the crash output specified a different $BINARY_PATH

src
 
=
 
Path
(
sys
.
argv
[
1
] 
if
 
len
(
sys
.
argv
) 
>
 
1
 
else
 
"/usr/local/bin/agy"
)

dst
 
=
 
Path
(
str
(
src
) 
+
 
".va39"
)

if
 
not
 
src
.
exists
():

 
raise
 
SystemExit
(
f"Input binary does not exist: 
{
src
}
"
)

print
(
f"Input binary : 
{
src
}
"
)

print
(
f"SHA256 in    : 
{
hashlib
.
sha256
(
src
.
read_bytes
()).
hexdigest
()
}
\n
"
)

shutil
.
copyfile
(
src
, 
dst
)

data
 
=
 
bytearray
(
dst
.
read_bytes
())

def
 
get
(
off
):

 
return
 
struct
.
unpack_from
(
"<I"
, 
data
, 
off
)[
0
]

def
 
put
(
off
, 
word
):

 
struct
.
pack_into
(
"<I"
, 
data
, 
off
, 
word
)

# 1. Find google_malloc section

lo
, 
hi
 
=
 
0
, 
len
(
data
)

if
 
data
[:
4
] 
==
 
b"
\x7f
ELF"
:

 
e_shoff
 
=
 
struct
.
unpack_from
(
"<Q"
, 
data
, 
40
)[
0
]

 
e_shentsize
 
=
 
struct
.
unpack_from
(
"<H"
, 
data
, 
58
)[
0
]

 
e_shnum
 
=
 
struct
.
unpack_from
(
"<H"
, 
data
, 
60
)[
0
]

 
e_shstrndx
 
=
 
struct
.
unpack_from
(
"<H"
, 
data
, 
62
)[
0
]

 
shstr_base
 
=
 
e_shoff
 
+
 
e_shstrndx
 
*
 
e_shentsize

 
shstr_off
 
=
 
struct
.
unpack_from
(
"<Q"
, 
data
, 
shstr_base
 
+
 
24
)[
0
]

 
for
 
i
 
in
 
range
(
e_shnum
):

 
base
 
=
 
e_shoff
 
+
 
i
 
*
 
e_shentsize

 
sh_name
 
=
 
struct
.
unpack_from
(
"<I"
, 
data
, 
base
)[
0
]

 
sh_offset
 
=
 
struct
.
unpack_from
(
"<Q"
, 
data
, 
base
 
+
 
24
)[
0
]

 
sh_size
 
=
 
struct
.
unpack_from
(
"<Q"
, 
data
, 
base
 
+
 
32
)[
0
]

 
try
:

 
nend
 
=
 
data
.
index
(
b"
\x00
"
, 
shstr_off
 
+
 
sh_name
)

 
section
 
=
 
data
[
shstr_off
 
+
 
sh_name
 : 
nend
].
decode
(
"utf-8"
)

 
if
 
section
 
==
 
"google_malloc"
:

 
lo
, 
hi
 
=
 
sh_offset
, 
sh_offset
 
+
 
sh_size

 
print
(
f"Found google_malloc section: 0x
{
lo
:x
}
 - 0x
{
hi
:x
}
"
)

 
break

 
except
 
ValueError
:

 
pass

# 2. ubfx #42,#3 -> #35,#3 and lsl #42 -> #35

ubfx_count
, 
lsl_count
 
=
 
0
, 
0

for
 
off
 
in
 
range
(
lo
, 
hi
, 
4
):

 
w
 
=
 
get
(
off
)

 
if
 (
w
 
&
 
0x7F800000
) 
==
 
0x53000000
:

 
immr
, 
imms
 
=
 (
w
 
>>
 
16
) 
&
 
0x3F
, (
w
 
>>
 
10
) 
&
 
0x3F

 
if
 
immr
 
==
 
42
 
and
 
imms
 
==
 
44
:

 
put
(
off
, (
w
 
&
 
~
((
0x3F
 
<<
 
16
) 
|
 (
0x3F
 
<<
 
10
))) 
|
 (
35
 
<<
 
16
) 
|
 (
37
 
<<
 
10
))

 
ubfx_count
 
+=
 
1

 
elif
 
immr
 
==
 
22
 
and
 
imms
 
==
 
21
:

 
put
(
off
, (
w
 
&
 
~
((
0x3F
 
<<
 
16
) 
|
 (
0x3F
 
<<
 
10
))) 
|
 (
29
 
<<
 
16
) 
|
 (
28
 
<<
 
10
))

 
lsl_count
 
+=
 
1

print
(
f"[1] ubfx patches : 
{
ubfx_count
}
\n
    lsl patches  : 
{
lsl_count
}
"
)

# 3. Random address mask pairs

mask_count
 
=
 
0

for
 
off
 
in
 
range
(
lo
, 
hi
 
-
 
4
, 
4
):

 
if
 
get
(
off
) 
==
 
0x92D3800A
 
and
 
get
(
off
 
+
 
4
) 
==
 
0xF2E0000A
:

 
put
(
off
, 
0x9280000A
)

 
put
(
off
 
+
 
4
, 
0xD35DFD4A
)

 
mask_count
 
+=
 
1

print
(
f"[2] Random mask  : 
{
mask_count
}
"
)

# 4. MmapAlignedLocked upper bound

mmap_count
 
=
 
0

for
 
off
 
in
 
range
(
lo
, 
hi
, 
4
):

 
if
 
get
(
off
) 
==
 
0xF2E00029
:

 
put
(
off
, 
0xD3596129
)

 
mmap_count
 
+=
 
1

print
(
f"[3] MmapAligned  : 
{
mmap_count
}
"
)

# 5. Inlined tag constants

word_rewrites
 
=
 {

 
0xD2C20009
: 
0xD2C00409
, 
0xD2C2000A
: 
0xD2C0040A
, 
0xF2C20008
: 
0xF2DFF408
,

 
0xF2C20009
: 
0xF2DFF409
, 
0xD2C10009
: 
0xD2C00209
, 
0xD2C1000A
: 
0xD2C0020A
,

 
0xF2C38008
: 
0xF2DFF708
, 
0xF2C38009
: 
0xF2DFF709
, 
0x92560A6C
: 
0x925D0A6C
,

 
0x92560A6A
: 
0x925D0A6A
, 
0xD2C3000D
: 
0xD2C0060D
, 
0xD2C3000C
: 
0xD2C0060C
,

 
0xD2C08008
: 
0xD2C00108
,

}

counts
 
=
 {
old
: 
0
 
for
 
old
 
in
 
word_rewrites
}

for
 
off
 
in
 
range
(
lo
, 
hi
, 
4
):

 
w
 
=
 
get
(
off
)

 
if
 
w
 
in
 
word_rewrites
:

 
put
(
off
, 
word_rewrites
[
w
])

 
counts
[
w
] 
+=
 
1

print
(
f"[4] Tag constants: 
{
sum
(
counts
.
values
())
}
 words rewritten"
)

dst
.
write_bytes
(
data
)

dst
.
chmod
(
0o755
)

print
(
f"
\n
SHA256 out   : 
{
hashlib
.
sha256
(
dst
.
read_bytes
()).
hexdigest
()
}
"
)

print
(
f"Output       : 
{
dst
}
\n
"
)

if
 
ubfx_count
 
==
 
0
 
or
 
mask_count
 
==
 
0
:

 
print
(
"WARNING: No patches applied - binary structure may have changed."
)

else
:

 
print
(
"Patch looks complete. To replace original and test:"
)

 
print
(
f"  sudo mv 
{
dst
}
 
{
src
}
"
)

 
print
(
f"  
{
src
}
 --version"
)
