Skip to content

Commit a4d61f6

Browse files
committed
rust: use strict provenance on rustc >= 1.84.0
Rust 1.84.0 stabilized the strict provenance APIs[1]. This patch enables the (unstable) lints `fuzzy_provenance_casts` and `lossy_provenance_casts` (available since Rust 1.61.0[2]) and uses strict provenance APIs where these lints triggered. The `kernel` crate is kept backwards-compatible by introducing forwarding functions at the root which are marked `#[allow(clippy::incompatible_msrv)]` to avoid warnings on rustc < 1.84.0. The discussion in the tracking Issue for strict_provenance_lints[3] seems to be nearing resolution with the only open question being: > do we really want two separate lints for the two directions? which seems minor enough that this is unlikely to cause significant churn when stabilized. This is limited to the `kernel` crate because adding these lints in the root `Makefile` causes `core` itself to be compiled with them, which in turn causes warnings on the implementations of the strict provenance APIs themselves. Link: https://blog.rust-lang.org/2025/01/09/Rust-1.84.0.html#strict-provenance-apis [1] Link: https://github.com/rust-lang/rust/blob/e71f9a9a98b0faf423844bf0ba7438f29dc27d58/compiler/rustc_feature/src/unstable.rs#L605 [2] Link: rust-lang/rust#130351 [3] Suggested-by: Benno Lossin <[email protected]> Link: https://lore.kernel.org/all/[email protected]/
1 parent 82eb67d commit a4d61f6

File tree

13 files changed

+83
-35
lines changed

13 files changed

+83
-35
lines changed

Makefile

+8-1
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,8 @@ export rust_common_flags := --edition=2021 \
473473
-Astable_features \
474474
-Dnon_ascii_idents \
475475
-Dunsafe_op_in_unsafe_fn \
476+
-Wfuzzy_provenance_casts \
477+
-Wlossy_provenance_casts \
476478
-Wmissing_docs \
477479
-Wrust_2018_idioms \
478480
-Wunreachable_pub \
@@ -498,7 +500,7 @@ KBUILD_HOSTCFLAGS := $(KBUILD_USERHOSTCFLAGS) $(HOST_LFS_CFLAGS) \
498500
KBUILD_HOSTCXXFLAGS := -Wall -O2 $(HOST_LFS_CFLAGS) $(HOSTCXXFLAGS) \
499501
-I $(srctree)/scripts/include
500502
KBUILD_HOSTRUSTFLAGS := $(rust_common_flags) -O -Cstrip=debuginfo \
501-
-Zallow-features= $(HOSTRUSTFLAGS)
503+
$(HOSTRUSTFLAGS)
502504
KBUILD_HOSTLDFLAGS := $(HOST_LFS_LDFLAGS) $(HOSTLDFLAGS)
503505
KBUILD_HOSTLDLIBS := $(HOST_LFS_LIBS) $(HOSTLDLIBS)
504506
KBUILD_PROCMACROLDFLAGS := $(or $(PROCMACROLDFLAGS),$(KBUILD_HOSTLDFLAGS))
@@ -870,6 +872,11 @@ KBUILD_CFLAGS += -Os
870872
KBUILD_RUSTFLAGS += -Copt-level=s
871873
endif
872874

875+
# Lints were moved to `strict_provenance_lints` when `strict_provenance` was stabilized.
876+
#
877+
# See https://github.com/rust-lang/rust/commit/56ee492a6e7a917b2b3f888e33dd52a13d3ecb64.
878+
export rustc_strict_provenance_feature := $(if $(CONFIG_RUSTC_HAS_STABLE_STRICT_PROVENANCE),strict_provenance_lints,strict_provenance)
879+
873880
# Always set `debug-assertions` and `overflow-checks` because their default
874881
# depends on `opt-level` and `debug-assertions`, respectively.
875882
KBUILD_RUSTFLAGS += -Cdebug-assertions=$(if $(CONFIG_RUST_DEBUG_ASSERTIONS),y,n)

init/Kconfig

+3
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ config CC_HAS_COUNTED_BY
132132
config RUSTC_HAS_COERCE_POINTEE
133133
def_bool RUSTC_VERSION >= 108400
134134

135+
config RUSTC_HAS_STABLE_STRICT_PROVENANCE
136+
def_bool RUSTC_VERSION >= 108400
137+
135138
config PAHOLE_VERSION
136139
int
137140
default $(shell,$(srctree)/scripts/pahole-version.sh $(PAHOLE))

rust/Makefile

+20-6
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,12 @@ endif
5757
core-cfgs = \
5858
--cfg no_fp_fmt_parse
5959

60+
rustc_strict_provenance_flags = -Zcrate-attr='feature($(rustc_strict_provenance_feature))'
61+
6062
quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $<
6163
cmd_rustdoc = \
6264
OBJTREE=$(abspath $(objtree)) \
63-
$(RUSTDOC) $(filter-out $(skip_flags),$(if $(rustdoc_host),$(rust_common_flags),$(rust_flags))) \
65+
$(RUSTDOC) $(filter-out $(skip_flags),$(if $(rustdoc_host),$(rust_common_flags),$(rust_flags)) $(rustc_strict_provenance_flags)) \
6466
$(rustc_target_flags) -L$(objtree)/$(obj) \
6567
-Zunstable-options --generate-link-to-definition \
6668
--output $(rustdoc_output) \
@@ -99,7 +101,7 @@ rustdoc-macros: $(src)/macros/lib.rs FORCE
99101

100102
# Starting with Rust 1.82.0, skipping `-Wrustdoc::unescaped_backticks` should
101103
# not be needed -- see https://github.com/rust-lang/rust/pull/128307.
102-
rustdoc-core: private skip_flags = -Wrustdoc::unescaped_backticks
104+
rustdoc-core: private skip_flags = -Wrustdoc::unescaped_backticks $(rustc_strict_provenance_flags)
103105
rustdoc-core: private rustc_target_flags = $(core-cfgs)
104106
rustdoc-core: $(RUST_LIB_SRC)/core/src/lib.rs FORCE
105107
+$(call if_changed,rustdoc)
@@ -122,6 +124,7 @@ quiet_cmd_rustc_test_library = $(RUSTC_OR_CLIPPY_QUIET) TL $<
122124
cmd_rustc_test_library = \
123125
OBJTREE=$(abspath $(objtree)) \
124126
$(RUSTC_OR_CLIPPY) $(rust_common_flags) \
127+
$(rustc_strict_provenance_flags) \
125128
@$(objtree)/include/generated/rustc_cfg $(rustc_target_flags) \
126129
--crate-type $(if $(rustc_test_library_proc),proc-macro,rlib) \
127130
--out-dir $(objtree)/$(obj)/test --cfg testlib \
@@ -155,11 +158,19 @@ rusttestlib-uapi: private rustc_target_flags = --extern ffi
155158
rusttestlib-uapi: $(src)/uapi/lib.rs rusttestlib-ffi FORCE
156159
+$(call if_changed,rustc_test_library)
157160

161+
# `rustdoc --test` doesn't respect `-Zcrate-attr`, which means we can't use
162+
# `rustc_strict_provenance_flags` below. Instead we filter out those lints to avoid unknown lint
163+
# warnings.
164+
#
165+
# See https://github.com/rust-lang/rust/issues/138491.
166+
rustc_strict_provenance_lints = -Wfuzzy_provenance_casts -Wlossy_provenance_casts
167+
158168
quiet_cmd_rustdoc_test = RUSTDOC T $<
159169
cmd_rustdoc_test = \
160170
RUST_MODFILE=test.rs \
161171
OBJTREE=$(abspath $(objtree)) \
162-
$(RUSTDOC) --test $(rust_common_flags) \
172+
$(RUSTDOC) --test \
173+
$(filter-out $(rustc_strict_provenance_lints),$(rust_common_flags)) \
163174
@$(objtree)/include/generated/rustc_cfg \
164175
$(rustc_target_flags) $(rustdoc_test_target_flags) \
165176
$(rustdoc_test_quiet) \
@@ -171,7 +182,8 @@ quiet_cmd_rustdoc_test_kernel = RUSTDOC TK $<
171182
rm -rf $(objtree)/$(obj)/test/doctests/kernel; \
172183
mkdir -p $(objtree)/$(obj)/test/doctests/kernel; \
173184
OBJTREE=$(abspath $(objtree)) \
174-
$(RUSTDOC) --test $(rust_flags) \
185+
$(RUSTDOC) --test \
186+
$(filter-out $(rustc_strict_provenance_lints),$(rust_flags)) \
175187
-L$(objtree)/$(obj) --extern ffi --extern kernel \
176188
--extern build_error --extern macros \
177189
--extern bindings --extern uapi \
@@ -193,6 +205,7 @@ quiet_cmd_rustc_test = $(RUSTC_OR_CLIPPY_QUIET) T $<
193205
cmd_rustc_test = \
194206
OBJTREE=$(abspath $(objtree)) \
195207
$(RUSTC_OR_CLIPPY) --test $(rust_common_flags) \
208+
$(rustc_strict_provenance_flags) \
196209
@$(objtree)/include/generated/rustc_cfg \
197210
$(rustc_target_flags) --out-dir $(objtree)/$(obj)/test \
198211
-L$(objtree)/$(obj)/test \
@@ -362,6 +375,7 @@ $(obj)/exports_kernel_generated.h: $(obj)/kernel.o FORCE
362375
quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@
363376
cmd_rustc_procmacro = \
364377
$(RUSTC_OR_CLIPPY) $(rust_common_flags) \
378+
$(rustc_strict_provenance_flags) \
365379
-Clinker-flavor=gcc -Clinker=$(HOSTCC) \
366380
-Clink-args='$(call escsq,$(KBUILD_PROCMACROLDFLAGS))' \
367381
--emit=dep-info=$(depfile) --emit=link=$@ --extern proc_macro \
@@ -376,7 +390,7 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L
376390
cmd_rustc_library = \
377391
OBJTREE=$(abspath $(objtree)) \
378392
$(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) \
379-
$(filter-out $(skip_flags),$(rust_flags) $(rustc_target_flags)) \
393+
$(filter-out $(skip_flags),$(rust_flags) $(rustc_target_flags) $(rustc_strict_provenance_flags)) \
380394
--emit=dep-info=$(depfile) --emit=obj=$@ \
381395
--emit=metadata=$(dir $@)$(patsubst %.o,lib%.rmeta,$(notdir $@)) \
382396
--crate-type rlib -L$(objtree)/$(obj) \
@@ -436,7 +450,7 @@ $(obj)/helpers/helpers.o: $(src)/helpers/helpers.c $(recordmcount_source) FORCE
436450
$(obj)/exports.o: private skip_gendwarfksyms = 1
437451

438452
$(obj)/core.o: private skip_clippy = 1
439-
$(obj)/core.o: private skip_flags = -Wunreachable_pub
453+
$(obj)/core.o: private skip_flags = -Wunreachable_pub -Wlossy_provenance_casts $(rustc_strict_provenance_flags)
440454
$(obj)/core.o: private rustc_objcopy = $(foreach sym,$(redirect-intrinsics),--redefine-sym $(sym)=__rust$(sym))
441455
$(obj)/core.o: private rustc_target_flags = $(core-cfgs)
442456
$(obj)/core.o: $(RUST_LIB_SRC)/core/src/lib.rs \

rust/kernel/alloc.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ pub unsafe trait Allocator {
217217

218218
/// Returns a properly aligned dangling pointer from the given `layout`.
219219
pub(crate) fn dangling_from_layout(layout: Layout) -> NonNull<u8> {
220-
let ptr = layout.align() as *mut u8;
220+
let ptr = crate::with_exposed_provenance_mut(layout.align());
221221

222222
// SAFETY: `layout.align()` (and hence `ptr`) is guaranteed to be non-zero.
223223
unsafe { NonNull::new_unchecked(ptr) }

rust/kernel/devres.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,14 @@ struct DevresInner<T> {
6464
/// return Err(ENOMEM);
6565
/// }
6666
///
67-
/// Ok(IoMem(IoRaw::new(addr as usize, SIZE)?))
67+
/// Ok(IoMem(IoRaw::new(kernel::expose_provenance(addr), SIZE)?))
6868
/// }
6969
/// }
7070
///
7171
/// impl<const SIZE: usize> Drop for IoMem<SIZE> {
7272
/// fn drop(&mut self) {
7373
/// // SAFETY: `self.0.addr()` is guaranteed to be properly mapped by `Self::new`.
74-
/// unsafe { bindings::iounmap(self.0.addr() as *mut c_void); };
74+
/// unsafe { bindings::iounmap(kernel::with_exposed_provenance_mut(self.0.addr())); };
7575
/// }
7676
/// }
7777
///

rust/kernel/io.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//! C header: [`include/asm-generic/io.h`](srctree/include/asm-generic/io.h)
66
77
use crate::error::{code::EINVAL, Result};
8-
use crate::{bindings, build_assert, ffi::c_void};
8+
use crate::{bindings, build_assert};
99

1010
/// Raw representation of an MMIO region.
1111
///
@@ -75,14 +75,14 @@ impl<const SIZE: usize> IoRaw<SIZE> {
7575
/// return Err(ENOMEM);
7676
/// }
7777
///
78-
/// Ok(IoMem(IoRaw::new(addr as usize, SIZE)?))
78+
/// Ok(IoMem(IoRaw::new(kernel::expose_provenance(addr), SIZE)?))
7979
/// }
8080
/// }
8181
///
8282
/// impl<const SIZE: usize> Drop for IoMem<SIZE> {
8383
/// fn drop(&mut self) {
8484
/// // SAFETY: `self.0.addr()` is guaranteed to be properly mapped by `Self::new`.
85-
/// unsafe { bindings::iounmap(self.0.addr() as *mut c_void); };
85+
/// unsafe { bindings::iounmap(kernel::with_exposed_provenance_mut(self.0.addr())); };
8686
/// }
8787
/// }
8888
///
@@ -119,7 +119,7 @@ macro_rules! define_read {
119119
let addr = self.io_addr_assert::<$type_name>(offset);
120120

121121
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
122-
unsafe { bindings::$name(addr as *const c_void) }
122+
unsafe { bindings::$name(crate::with_exposed_provenance(addr)) }
123123
}
124124

125125
/// Read IO data from a given offset.
@@ -131,7 +131,7 @@ macro_rules! define_read {
131131
let addr = self.io_addr::<$type_name>(offset)?;
132132

133133
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
134-
Ok(unsafe { bindings::$name(addr as *const c_void) })
134+
Ok(unsafe { bindings::$name(crate::with_exposed_provenance(addr)) })
135135
}
136136
};
137137
}
@@ -148,7 +148,7 @@ macro_rules! define_write {
148148
let addr = self.io_addr_assert::<$type_name>(offset);
149149

150150
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
151-
unsafe { bindings::$name(value, addr as *mut c_void) }
151+
unsafe { bindings::$name(value, crate::with_exposed_provenance_mut(addr)) }
152152
}
153153

154154
/// Write IO data from a given offset.
@@ -160,7 +160,7 @@ macro_rules! define_write {
160160
let addr = self.io_addr::<$type_name>(offset)?;
161161

162162
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
163-
unsafe { bindings::$name(value, addr as *mut c_void) }
163+
unsafe { bindings::$name(value, crate::with_exposed_provenance_mut(addr)) }
164164
Ok(())
165165
}
166166
};

rust/kernel/lib.rs

+20
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,26 @@
2525
#![feature(const_ptr_write)]
2626
#![feature(const_refs_to_cell)]
2727

28+
#[allow(clippy::incompatible_msrv)]
29+
mod strict_provenance {
30+
#[doc(hidden)]
31+
pub fn expose_provenance<T>(addr: *const T) -> usize {
32+
addr.expose_provenance()
33+
}
34+
35+
#[doc(hidden)]
36+
pub fn with_exposed_provenance<T>(addr: usize) -> *const T {
37+
core::ptr::with_exposed_provenance(addr)
38+
}
39+
40+
#[doc(hidden)]
41+
pub fn with_exposed_provenance_mut<T>(addr: usize) -> *mut T {
42+
core::ptr::with_exposed_provenance_mut(addr)
43+
}
44+
}
45+
46+
pub use strict_provenance::*;
47+
2848
// Ensure conditional compilation based on the kernel configuration works;
2949
// otherwise we may silently break things like initcall handling.
3050
#[cfg(not(CONFIG_RUST))]

rust/kernel/of.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ unsafe impl RawDeviceId for DeviceId {
2222
const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::of_device_id, data);
2323

2424
fn index(&self) -> usize {
25-
self.0.data as usize
25+
crate::expose_provenance(self.0.data)
2626
}
2727
}
2828

rust/kernel/pci.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ impl<const SIZE: usize> Bar<SIZE> {
287287
// `pdev` is valid by the invariants of `Device`.
288288
// `num` is checked for validity by a previous call to `Device::resource_len`.
289289
// `name` is always valid.
290-
let ioptr: usize = unsafe { bindings::pci_iomap(pdev.as_raw(), num, 0) } as usize;
290+
let ioptr = crate::expose_provenance(unsafe { bindings::pci_iomap(pdev.as_raw(), num, 0) });
291291
if ioptr == 0 {
292292
// SAFETY:
293293
// `pdev` valid by the invariants of `Device`.
@@ -320,7 +320,7 @@ impl<const SIZE: usize> Bar<SIZE> {
320320
// `ioptr` is valid by the safety requirements.
321321
// `num` is valid by the safety requirements.
322322
unsafe {
323-
bindings::pci_iounmap(pdev.as_raw(), ioptr as *mut kernel::ffi::c_void);
323+
bindings::pci_iounmap(pdev.as_raw(), crate::with_exposed_provenance_mut(ioptr));
324324
bindings::pci_release_region(pdev.as_raw(), num);
325325
}
326326
}

rust/kernel/str.rs

+6-10
Original file line numberDiff line numberDiff line change
@@ -692,9 +692,9 @@ impl RawFormatter {
692692
pub(crate) unsafe fn from_ptrs(pos: *mut u8, end: *mut u8) -> Self {
693693
// INVARIANT: The safety requirements guarantee the type invariants.
694694
Self {
695-
beg: pos as usize,
696-
pos: pos as usize,
697-
end: end as usize,
695+
beg: crate::expose_provenance(pos),
696+
pos: crate::expose_provenance(pos),
697+
end: crate::expose_provenance(end),
698698
}
699699
}
700700

@@ -705,7 +705,7 @@ impl RawFormatter {
705705
/// The memory region starting at `buf` and extending for `len` bytes must be valid for writes
706706
/// for the lifetime of the returned [`RawFormatter`].
707707
pub(crate) unsafe fn from_buffer(buf: *mut u8, len: usize) -> Self {
708-
let pos = buf as usize;
708+
let pos = crate::expose_provenance(buf);
709709
// INVARIANT: We ensure that `end` is never less then `buf`, and the safety requirements
710710
// guarantees that the memory region is valid for writes.
711711
Self {
@@ -719,7 +719,7 @@ impl RawFormatter {
719719
///
720720
/// N.B. It may point to invalid memory.
721721
pub(crate) fn pos(&self) -> *mut u8 {
722-
self.pos as *mut u8
722+
crate::with_exposed_provenance_mut(self.pos)
723723
}
724724

725725
/// Returns the number of bytes written to the formatter.
@@ -741,11 +741,7 @@ impl fmt::Write for RawFormatter {
741741
// SAFETY: If `len_to_copy` is non-zero, then we know `pos` has not gone past `end`
742742
// yet, so it is valid for write per the type invariants.
743743
unsafe {
744-
core::ptr::copy_nonoverlapping(
745-
s.as_bytes().as_ptr(),
746-
self.pos as *mut u8,
747-
len_to_copy,
748-
)
744+
core::ptr::copy_nonoverlapping(s.as_bytes().as_ptr(), self.pos(), len_to_copy)
749745
};
750746
}
751747

rust/kernel/uaccess.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,9 @@ impl UserSliceReader {
226226
}
227227
// SAFETY: `out_ptr` points into a mutable slice of length `len`, so we may write
228228
// that many bytes to it.
229-
let res = unsafe { bindings::copy_from_user(out_ptr, self.ptr as *const c_void, len) };
229+
let res = unsafe {
230+
bindings::copy_from_user(out_ptr, crate::with_exposed_provenance(self.ptr), len)
231+
};
230232
if res != 0 {
231233
return Err(EFAULT);
232234
}
@@ -264,7 +266,7 @@ impl UserSliceReader {
264266
let res = unsafe {
265267
bindings::_copy_from_user(
266268
out.as_mut_ptr().cast::<c_void>(),
267-
self.ptr as *const c_void,
269+
crate::with_exposed_provenance(self.ptr),
268270
len,
269271
)
270272
};
@@ -330,7 +332,9 @@ impl UserSliceWriter {
330332
}
331333
// SAFETY: `data_ptr` points into an immutable slice of length `len`, so we may read
332334
// that many bytes from it.
333-
let res = unsafe { bindings::copy_to_user(self.ptr as *mut c_void, data_ptr, len) };
335+
let res = unsafe {
336+
bindings::copy_to_user(crate::with_exposed_provenance_mut(self.ptr), data_ptr, len)
337+
};
334338
if res != 0 {
335339
return Err(EFAULT);
336340
}
@@ -357,7 +361,7 @@ impl UserSliceWriter {
357361
// is a compile-time constant.
358362
let res = unsafe {
359363
bindings::_copy_to_user(
360-
self.ptr as *mut c_void,
364+
crate::with_exposed_provenance_mut(self.ptr),
361365
(value as *const T).cast::<c_void>(),
362366
len,
363367
)

scripts/Makefile.build

+1-1
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ $(obj)/%.lst: $(obj)/%.c FORCE
226226
# Compile Rust sources (.rs)
227227
# ---------------------------------------------------------------------------
228228

229-
rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons
229+
rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,$(rustc_strict_provenance_feature)
230230

231231
# `--out-dir` is required to avoid temporaries being created by `rustc` in the
232232
# current working directory, which may be not accessible in the out-of-tree

scripts/Makefile.host

+4
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,14 @@ hostcxx_flags = -Wp,-MMD,$(depfile) \
8787
$(KBUILD_HOSTCXXFLAGS) $(HOST_EXTRACXXFLAGS) \
8888
$(HOSTCXXFLAGS_$(target-stem).o)
8989

90+
rust_allowed_features := $(rustc_strict_provenance_feature)
91+
9092
# `--out-dir` is required to avoid temporaries being created by `rustc` in the
9193
# current working directory, which may be not accessible in the out-of-tree
9294
# modules case.
9395
hostrust_flags = --out-dir $(dir $@) --emit=dep-info=$(depfile) \
96+
-Zallow-features=$(rust_allowed_features) \
97+
-Zcrate-attr='feature($(rust_allowed_features))' \
9498
-Clinker-flavor=gcc -Clinker=$(HOSTCC) \
9599
-Clink-args='$(call escsq,$(KBUILD_HOSTLDFLAGS))' \
96100
$(KBUILD_HOSTRUSTFLAGS) $(HOST_EXTRARUSTFLAGS) \

0 commit comments

Comments
 (0)